Правильное использование хранилища строк в C и С++

Популярные разработчики программного обеспечения и компании (Joel Spolsky, Программное обеспечение Fog Creek) имеют тенденцию использовать wchar_t для хранения символов Unicode при написании кода C или С++. Когда и как следует использовать char и wchar_t в отношении хороших методов кодирования?

Меня особенно интересует соответствие POSIX при написании программного обеспечения, которое использует Unicode.

При использовании wchar_t вы можете искать символы в массиве широких символов на основе каждого элемента или каждого элемента массива:

/* C code fragment */
const wchar_t *overlord = L"ov€rlord";
if (overlord[2] == L'€')
    wprintf(L"Character comparison on a per-character basis.\n");

Как вы можете сравнивать байты Unicode (или символы) при использовании char?

До сих пор мой предпочтительный способ сравнения строк и символов типа char в C часто выглядит так:

/* C code fragment */
const char *mail[] = { "ov€[email protected]", "ov€[email protected]" };
if (mail[0][2] == mail[1][2] && mail[0][3] == mail[1][3] && mail[0][3] == mail[1][3])
    printf("%s\n%zu", *mail, strlen(*mail));

Этот метод проверяет эквивалент байта символа юникода. Символ Unicode Euro занимает 3 байта. Поэтому нужно сравнить три байта массива char, чтобы узнать, соответствуют ли символы Unicode. Часто вам нужно знать размер символа или строки, которую вы хотите сравнить, и биты, которые она создает для решения. Это не похоже на хороший способ обработки Unicode. Есть ли лучший способ сравнения строк и символьных элементов типа char?

Кроме того, при использовании wchar_t, как вы можете сканировать содержимое файла в массив? Функция fread не дает достоверных результатов.

Ответ 1

Если вы знаете, что имеете дело с unicode, то не подходят char и wchar_t, так как их размеры определяются компилятором/платформой. Например, wchar_t - 2 байта в Windows (MSVC), но 4 байта в Linux (GCC). Стандарты C11 и С++ 11 были немного более строгими и определяют два новых типа символов (char16_t и char32_t) с соответствующими литеральными префиксами для создания строк UTF- {8, 16, 32}.

Если вам нужно хранить и манипулировать символами юникода, вы должны использовать библиотеку, предназначенную для задания, поскольку ни один из языков языка pre-C11 и pre-С++ 11 не был написан с учетом unicode. Есть несколько, чтобы выбрать из, но ICU (и поддерживает C, С++ и Java).

Ответ 2

Меня особенно интересует соблюдение POSIX при написании программного обеспечения который использует Unicode.

В этом случае вы, вероятно, захотите использовать UTF-8 (с char) в качестве предпочтительного типа строки Юникода. POSIX не имеет большого количества функций для работы с wchar_t — что в основном вещь Windows.

Этот метод проверяет эквивалент байта символа юникода. Unicode Euro symbol € занимает 3 байта. Поэтому нужно сравнить три байта массива char, чтобы узнать, соответствуют ли символы Юникода. Часто вам нужно знать размер символа или строки, которую вы хотите сравнить и биты, которые он создает для работы решения.

Нет, нет. Вы просто сравниваете байты. Iff соответствует байтам, строки соответствуют. strcmp работает также с UTF-8, как и с любой другой кодировкой.

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

Ответ 3

Вы никогда не должны сравнивать байты или даже точки кода, чтобы определить, равны ли строки. Это из-за большого количества строк может быть идентичным с точки зрения пользователя, не будучи идентичным с точки зрения кодовой точки.