Выбрать главу

Поскольку каждая категория может быть установлена индивидуально, автор приложения решает, насколько будет программа использовать локаль. Например, если main() делает лишь это —

setlocale(LC_TIME, "");

 /* Использование локали только для времени и все */

— тогда, независимо от установленных в окружении других переменных LC_xxx, локали подчиняются лишь функции времени и даты. Все остальные действуют так, как если бы программа по-прежнему работала в локали «С». Сходным образом вызов:

setlocale(LC_TIME, "it_IT"); /* Время всегда итальянское */

заменяет переменную окружения LC_TIME (также, как LC_ALL), заставляя программу использовать для вычислений времени/даты данные для Италии. (Хотя Италия может быть прекрасным местом, программам лучше использовать "", чтобы они могли корректно работать везде; этот пример предназначен лишь для объяснения того, как работает setlocale().)

Можно индивидуально вызывать setlocale() для каждой категории, но простейшим способом является установка всего одним махом:

/* Находясь в Риме, вместо «всего» делайте все как римляне. :-) */

setlocale(LC_ALL, "");

Возвращаемое setlocale() значение является текущей установкой локали. Это либо строковое значение, переданное в предыдущем вызове, либо непрозрачное значение, представляющее используемую вначале локаль. Это самое значение может быть затем передано обратно setlocale(). Для последующего использования возвращаемое значение должно быть скопировано в локальное хранилище, поскольку это указатель на внутренние данные.

char *initial_locale;

initial_locale = strdup(setlocale(LC_ALL, "")); /* сохранить копию */

...

(void)setlocale(LC_ALL, initial_locale); /* восстановить ее */

Здесь мы сохранили копию, использовав функцию POSIX strdup() (см. раздел 3.2.2 «Копирование строк: strdup()»).

13.2.3. Сравнение строк: strcoll() и strxfrm()

Знакомая функция strcmp() сравнивает две строки, возвращая отрицательное, нулевое или положительное значения, если первая строка меньше, равна или больше второй. Это сравнение основано на числовых значениях символов в машинном наборе символов. Из-за этого результаты strcmp() никогда не изменяются.

Однако, при наличии локалей простого числового сравнения недостаточно. Каждая локаль определяет для содержащихся в ней символов последовательность сортировки, другими словами, относительный порядок символов внутри локали. Например, в простом 7-битном ASCII у двух символов 'А' и 'а' десятичные значения равны 65 и 97 соответственно. Соответственно, во фрагменте

int i = strcmp("А", "a");

i имеет отрицательное значение. Однако, в локали "en_US.UTF-8" 'A' идет после 'a', а не перед ним. Таким образом, использование strcmp() для приложений, использующих локаль, является плохой мыслью, мы могли бы сказать, что она возвращает игнорирующий локаль ответ.

Функция strcoll() (string collate — сортировка строк) существует для сравнения строк с использованием локали:

#include <string.h> /* ISO С */

int strcoll(const char *s1, const char *s2);

Она возвращает такие же отрицательные/нулевые/положительные значения, что и strcmp(). Следующая программа, ch13-compare.c, интерактивно демонстрирует разницу:

1  /* ch13-compare.с --- демонстрация strcmp() против strcoll() */

2

3  #include <stdio.h>

4  #include <locale.h>

5  #include <string.h>

6

7  int main(void)

8  {

9  #define STRBUFSIZE 1024

10  char locale[STRBUFSIZE], curloc[STRBUFSIZE];

11  char left[STRBUFSIZE], right[STRBUFSIZE];

12  char buf[BUFSIZ];

13  int count;

14

15  setlocale(LC_ALL, ""); /* установить локаль */

16  strcpy(curloc, setlocale(LC_ALL, NULL)); /* сохранить ее */

17

18  printf("--> "); fflush(stdout);

19  while (fgets(buf, sizeof buf, stdin) != NULL) {

20   locale[0] = '\0';

21   count = sscanf(buf, "%s %s %s", left, right, locale);

22   if (count < 2)

23    break;

24

25   if (*locale) {

26    setlocale(LC_ALL, locale);

27    strcpy(curloc, locale);

28   }

29

30   printf("%s: strcmp(\"%s\", \"%s\") is %d\n", curloc, left,

31    right, strcmp(left, right));

32   printf("%s: strcoll(\"%s\", \"%s\") is %d\n", curloc, left,

33    right, strcoll(left, right));

34

35   printf("\n--> "); fflush(stdout);

36  }

37

38  exit(0);

39 }

Программа читает входные строки, состоящие из двух сравниваемых слов и необязательной локали, использующейся для сравнения. Если локаль дана, она становится локалью для последующих элементов. Программа начинает с любой локалью, которая установлена в окружении.

Массив curloc сохраняет текущую локаль для вывода результатов; left и right являются левым и правым сравниваемыми словами (строки 10–11). Основную часть программы составляет цикл (строки 19–36), который читает строки и выполняет работу. Строки 20–23 разделяют входную строку, locale инициализируется пустой строкой, если третья строка не предусмотрена.

Строки 25–28 устанавливают новую локаль, если она приведена. Строки 30–33 выводят результаты сравнения, а строка 35 приглашает для дальнейшего ввода. Вот демонстрация:

$ ch13-compare /* Запуск программы */

--> ABC abc /* Ввести два слова */

С: strcmp("ABC", "abc") is -1 /* Программа началась в локали "С" */

С: strcoll("ABC", "abc") is -1 /* В локали "С" идентичные рез-ты */

--> ABC abc en_US /* Слова те же, локаль "en_US" */

en_US: strcmp("ABC", "abc") is -1 /* strcmp() без изменений */

en_US: strcoll("ABC", "abc") is 2 /* рез-ты strcoll() изменились' */

--> ABC abc en_US.UTF-8 /* Слова те же, локаль "en_US.UTF-8" */

en_US.UTF-8: strcmp("ABC", "abc") is -1

en_US. UTF-8: strcoll("ABC", "abc") is 6

 /* Другое значение, все еще положительное */

--> junk JUNK /* Новые слова */

en_US.UTF-8: strcmp("junk", "JUNK") is 1 /* предыдущая локаль */