Хрестоматия по программированию на Си в Unix


Мобильность и машинная зависимость программ. Проблемы с русскими буквами.


В современных UNIX-ах с поддержкой различных языков таблица ctype загружается из некоторых системных файлов - для каждого языка своя. Для какого языка - выбирается по содержимому переменной окружения LANG. Если переменная не задана - используется значение "C", английский язык. Загрузка таблиц должна происходить явно, вызовом

... #include <locale.h>

... main(){ setlocale(LC_ALL, ""); ... все остальное ... }

Вернемся к нашей любимой проблеме со знаковым битом у типа char.

#include <stdio.h>

#include <locale.h>

#include <ctype.h>

int main(int ac, char *av[]){ char c; char *string = "абвгдежзиклмноп"; setlocale(LC_ALL, ""); for(;c = *string;string++){ #ifdef DEBUG printf("%c %d %d\n", *string, *string, c); #endif if(isprint(c)) printf("%c - печатный символ\n", c); } return 0; }

Эта программа неожиданно печатает

% a.out в - печатный символ з - печатный символ

И все. В чем дело???

Рассмотрим к примеру символ 'г'. Его код '\307'. В операторе

c = *string;

Символ c получает значение -57 (десятичное), которое ОТРИЦАТЕЛЬНО. В системном файле /usr/include/ctype.h макрос isprint определен так:

#define isprint(c) ((_ctype + 1)[c] & (_P|_U|_L|_N|_B))

И значение c используется в нашем случае как отрицательный индекс в массиве, ибо индекс приводится к типу int (signed). Откуда теперь извлекается значение флагов нам неизвестно; можно только с уверенностью сказать, что НЕ из массива _ctype.

Проблему решает либо использование

isprint(c & 0xFF) либо

isprint((unsigned char) c)

либо объявление в нашем примере

unsigned char c;

В первом случае мы явно приводим signed к unsigned битовой операцией, обнуляя лишние биты. Во втором и третьем - unsigned char расширяется в unsigned int, который останется положительным. Вероятно, второй путь предпочтительнее.

Итак, снова напомним, что русские буквы char, а не unsigned char дают отрицательные индексы в массиве.




- Начало -  - Назад -  - Вперед -