Что именно может представлять wchar_t? - PullRequest
0 голосов
/ 18 мая 2018

Согласно документу cppreference.com на wchar_t:

wchar_t - тип для представления широких символов (см. Широкие строки).Требуется быть достаточно большим, чтобы представлять любую поддерживаемую символьную кодовую точку (32 бита в системах, поддерживающих Юникод. Заметным исключением является Windows, где wchar_t составляет 16 бит и содержит кодовые единицы UTF-16). Он имеет одинаковый размер, подпись и выравнивание.как один из целочисленных типов, но это отдельный тип.

Стандарт гласит: [basic.fundamental]/5:

Тип wchar_­tотдельный тип, значения которого могут представлять различные коды для всех членов самого большого расширенного набора символов, указанного среди поддерживаемых локалей.Тип wchar_­t должен иметь те же требования к размеру, подписи и выравниванию, что и один из других интегральных типов, называемый его базовым типом.Типы char16_­t и char32_­t обозначают различные типы с таким же размером, подписью и выравниванием, что и uint_­least16_­t и uint_­least32_­t соответственно в <cstdint>, называемые базовыми типами.

Итак, , если я хочу иметь дело с символами Юникода, я должен использовать wchar_t?

Эквивалентно, как узнать, поддерживается ли определенный символ Юникода "" от wchar_t?

Ответы [ 5 ]

0 голосов
/ 26 мая 2018

Все зависит от того, что вы подразумеваете под «иметь дело», но одно можно сказать наверняка: в том, что касается Unicode std::basic_string вообще не предоставляет никакой реальной функциональности.

В любой конкретной программе,вам потребуется выполнить X операций с поддержкой Unicode, например, интеллектуальное сопоставление строк, сложение регистра, регулярное выражение, поиск разрывов слов, использование строки Unicode в качестве имени пути и т. д.

Поддержка этих операцийпочти всегда будет какая-то библиотека и / или собственный API, предоставляемый платформой, и цель для меня будет состоять в том, чтобы хранить и манипулировать моими строками таким образом, чтобы эти операции могли выполняться без разброса знаний о базовой библиотекеи поддержка нативного API во всем коде больше, чем необходимо.Я также хотел бы рассчитывать на будущее в отношении ширины символов, которые я храню в своих строках, на случай, если я передумаю.

Предположим, например, что вы решили использовать ICU делать тяжелую работу.Сразу же возникает очевидная проблема: icu::UnicodeString никак не связан с std::basic_string.Что делать?Работать исключительно с icu::UnicodeString по всему коду?Возможно, нет.

Или, может быть, фокус приложения переключается с европейских языков на азиатские, так что UTF-16 становится (возможно) лучшим выбором, чем UTF-8.

Итак, мойможно было бы использовать пользовательский строковый класс, полученный из std::basic_string, что-то вроде этого:

typedef wchar_t mychar_t;  // say

class MyString : public std::basic_string <mychar_t>
{
...
};

Сразу же вы можете гибко выбирать размер кодовых единиц, хранящихся в вашем контейнере.,Но вы можете сделать гораздо больше, чем это.Например, с указанным выше объявлением (и после добавления в шаблон для различных конструкторов, которые вам нужно предоставить для пересылки их в std::basic_string), вы все равно не можете сказать:

MyString s = "abcde";

Потому что «abcde»является узкой строкой и различные конструкторы для std::basic_string <wchar_t> ожидают широкой строки.Microsoft решает эту проблему с помощью макроса (TEXT ("...") или __T ("...")), но это боль.Все, что нам нужно сделать сейчас, это предоставить подходящий конструктор в MyString с подписью MyString (const char *s), и проблема будет решена.

На практике этот конструктор, вероятно, будет ожидать строку UTF-8, независимо от того,основной ширины символа, используемой для MyString, и конвертируйте ее при необходимости.Кто-то комментирует здесь где-то, что вы должны хранить ваши строки как UTF-8, чтобы вы могли создавать их из литералов UTF-8 в вашем коде.Что ж, теперь мы нарушили это ограничение.Базовая ширина символов в наших строках может быть любой, какой мы захотим.

Еще одна вещь, о которой говорили в этой теме, это то, что find_first_of может не работать должным образом для строк UTF-8 (и, действительно, некоторых UTF-16 тоже).Что ж, теперь вы можете предоставить реализацию, которая делает свою работу правильно.Должно занять около получаса.Если в std::basic_string есть другие «сломанные» реализации (и я уверен, что они есть), то большинство из них, вероятно, могут быть заменены с такой же легкостью.

Что касается остальных, то в основном это зависит от того, какой уровеньабстракции вы хотите реализовать в своем классе MyString.Если ваше приложение удовлетворено, например, зависимостью от ICU, то вы можете просто предоставить несколько методов для преобразования в icu::UnicodeString.Вероятно, именно так и поступит большинство людей.

Или, если вам нужно передать строки UTF-16 в / из собственных API-интерфейсов Windows, вы можете добавить методы для преобразования в const WCHAR * и обратно (что опять-таки вы бы реализовали втаким образом, что они работают для всех значений mychar_t).Или вы можете пойти дальше и абстрагироваться от части или всей поддержки Unicode, предоставляемой используемой платформой и библиотекой.Mac, например, имеет богатую поддержку Unicode, но он доступен только в Objective-C, поэтому вам нужно обернуть его.Это зависит от того, насколько переносимым должен быть ваш код.

Таким образом, вы можете добавлять любые функциональные возможности, которые вам нравятся, возможно, на постоянной основе по мере выполнения работы, не теряя способности носить свои струны как std::basic_string.Так или иначе.Просто попробуйте не писать код, который предполагает, что он знает, насколько он широк, или что он не содержит суррогатных пар .

0 голосов
/ 23 мая 2018

Прежде всего, вы должны проверить (как вы указали в своем вопросе), используете ли вы Windows и Visual Studio C ++ с wchar_t 16 битами, потому что в этом случае, чтобы использовать полную поддержку Юникода, вам потребуетсяпринять кодировку UTF-16.

Основной проблемой здесь является не sizeof wchar_t, который вы используете, но если библиотеки, которые вы собираетесь использовать, поддерживают полную поддержку Юникода.

Java имеетаналогичная проблема, поскольку его тип char имеет ширину 16 бит, поэтому он не может априори поддерживать полное пространство Юникода, но он поддерживает , поскольку использует кодировку UTF-16 ипара заменяет полные 24-битные кодовые точки.

Стоит также отметить, что UNICODE использует только верхнюю плоскость для кодирования редких кодовых точек, которые обычно не используются ежедневно.

Для поддержки юникода в любом случае, вам нужно использовать широкие наборы символов, поэтому wchar_t - хорошее начало.Если вы собираетесь работать с Visual Studio, то вы должны проверить, как библиотеки работают с символами Unicode.

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

Существуют широкие символьные функции для почти любой функции str*(3), а также для любой библиотечной функции stdio.h для работы с wchar_t s.Небольшое копание в файле /usr/include/wchar.h покажет имена подпрограмм.Перейдите на страницы руководства для документации по ним: fgetws(3), fputwc(3), fputws(3), fwide(3), fwprintf(3), ...

Наконец, еще раз подумайте, что, если вы имеете дело сMicrosoft Visual C ++, у вас другая реализация с самого начала.Даже если они полностью соответствуют стандарту 1035 *, вам придется столкнуться с некоторыми особенностями другой реализации.Возможно, для некоторых целей у вас будут разные имена функций.

0 голосов
/ 20 мая 2018

Итак, если я хочу иметь дело с символами Юникода, я должен использовать wchar_t?

Это зависит от того, с какой кодировкой вы имеете дело.В случае UTF-8 у вас все в порядке с char и std :: string.UTF- 8 означает, что наименьшая единица кодирования составляет 8 битов: все кодовые точки Unicode от U + 0000 до U + 007F кодируются только 1 байтом.Начиная с кодовой точки U + 0080, UTF-8 использует 2 байта для кодирования, начиная с U + 0800, он использует 3 байта, а с U + 10000 4 байта.Для обработки этой переменной ширины (1 байт - 2 байта - 3 байта - 4 байта) символ подходит лучше всего.Имейте в виду, что C-функции, такие как strlen, будут предоставлять результаты на основе байтов: «öö» на самом деле является 2-символьным текстом, но strlen вернет 4, потому что «ö» кодируется в 0xC3B6.

UTF- 16 означает, что наименьшая единица кодирования составляет 16 битов: все кодовые точки от U + 0000 до U + FFFF кодируются 2 байтами;начиная с U + 100000 используется 4 байта.В случае UTF-16 вы должны использовать wchar_t и std :: wstring, потому что большинство символов, с которыми вы когда-либо столкнетесь, будут закодированы в 2 байта.При использовании wchar_t вы больше не можете использовать C-функции, такие как strlen;Вы должны использовать широкие эквиваленты символов, такие как wcslen.

При использовании Visual Studio и построении с конфигурацией "Unicode" вы получите UTF-16: TCHAR и CString будут основаны на wchar_t вместо char.

0 голосов
/ 21 мая 2018

wchar_t используется в Windows, которая использует формат UTF16-LE.wchar_t требует широких функций символа.Например, wcslen(const wchar_t*) вместо strlen(const char*) и std::wstring вместо std::string

Машины на базе Unix (Linux, Mac и т. Д.) Используют UTF8.При этом используется char для хранения и те же функции C и C ++ для ASCII, как strlen(const char*) и std::string (см. Комментарии ниже о std::find_first_of)

wchar_t составляет 2 байта (UTF16) в Windows.Но на других машинах это 4 байта (UTF32).Это делает вещи более запутанными.

Для UTF32 вы можете использовать std::u32string, что одинаково для разных систем.


Вы можете рассмотреть возможность конвертации UTF8 в UTF32, потому что таким образом каждыйсимвол всегда 4 байта, и вы можете подумать, что строковые операции будут проще.Но это редко необходимо.

UTF8 разработан таким образом, что символы ASCII в диапазоне от 0 до 128 не используются для представления других кодовых точек Unicode.Это включает escape-последовательность '\', printf спецификаторы формата и общие символы синтаксического анализа, такие как ,

. Рассмотрим следующую строку UTF8.Допустим, вы хотите найти запятую

std::string str = u8"汉,?"; //3 code points represented by 8 bytes

Значение ASCII для запятой - 44, а str гарантированно содержит только один байт со значением 44.Чтобы найти запятую, вы можете просто использовать любую стандартную функцию в C или C ++ для поиска ','

Чтобы найти , вы можете искать строку u8"汉", так как эта кодовая точка не может быть представленакак один персонаж.

Некоторые функции C и C ++ не работают гладко с UTF8.К ним относятся

strtok
strspn
std::find_first_of

Аргументом для вышеуказанных функций является набор символов, а не действительная строка.

Так что str.find_first_of(u8"汉") не работает.Потому что u8"汉" составляет 3 байта, а find_first_of будет искать любой из этих байтов.Существует вероятность, что один из этих байтов используется для представления другой кодовой точки.

С другой стороны, str.find_first_of(u8",;abcd") является безопасным, поскольку все символы в аргументе поиска являются ASCII (* str)может содержать любой символ Unicode)

В редких случаях может потребоваться UTF32 (хотя я не могу себе представить, где!) Вы можете использовать std::codecvt для преобразования UTF8 в UTF32 для выполнения следующих операций:

std::u32string u32 = U"012汉"; //4 code points, represented by 4 elements
cout << u32.find_first_of(U"汉") << endl; //outputs 3
cout << u32.find_first_of(U'汉') << endl; //outputs 3

Примечание:

Вы должны использовать "Unicode везде" , а не "UTF8 везде" .

InLinux, Mac и т. Д. Используют UTF8 для Unicode.

В Windows используйте UTF16 для Unicode.Программисты Windows используют UTF16, они не делают бессмысленных преобразований туда и обратно в UTF8.Но есть законные случаи использования UTF8 в Windows.

Программист Windows, как правило, использует UTF8 для сохранения файлов, веб-страниц и т. Д. Так что для программистов, не являющихся Windows, это не беспокоит с точки зрения совместимости.1076 * Самому языку не важно, какой формат Unicode вы хотите использовать, но с точки зрения практичности используйте формат, который соответствует системе, с которой вы работаете.

0 голосов
/ 18 мая 2018

Итак, , если я хочу работать с символами Юникода, я должен использовать wchar_t?

Прежде всего, обратите внимание, что кодировка не вызываетВы должны использовать любой конкретный тип для представления определенного символа.Вы можете использовать char для представления символов Unicode так же, как wchar_t - вам нужно только помнить, что до 4 char с вместе сформируют допустимую кодовую точку в зависимости от UTF-8, UTF-16 или UTF-32, в то время как wchar_t может использовать 1 (UTF-32 в Linux и т. Д.) Или до 2 (UTF-16 в Windows).

Далее нет определенной кодировки Unicode.Некоторые кодировки Unicode используют фиксированную ширину для представления кодовых точек (например, UTF-32), другие (например, UTF-8 и UTF-16) имеют переменную длину (например, буква «a», безусловно, будет использовать только 1 байт, но отдельно).от английского алфавита, другие символы обязательно будут использовать больше байтов для представления).

Таким образом, вы должны решить, какие символы вы хотите представить, и затем выбрать соответствующую кодировку.В зависимости от типа символов, которые вы хотите представить, это повлияет на количество байтов, которые будут занимать ваши данные.Например, использование UTF-32 для представления в основном английских символов приведет к множеству 0-байтов.UTF-8 - лучший выбор для многих латинских языков, в то время как UTF-16 обычно является лучшим выбором для языков Восточной Азии.

После того как вы определились с этим, вы должны минимизировать количество конверсий и оставаться последовательнымис вашим решением.

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

Если вы хотели бы сделатьманипулирование текстом / интерпретация на основе кодовой точки, char определенно не тот путь, если у вас есть, например, японское кандзи.Но если вы просто хотите сообщить свои данные и больше не рассматривать их как количественную последовательность байтов, вы можете просто пойти с char.

Ссылка на UTF-8 везде былауже опубликовано в качестве комментария, и я предлагаю вам посмотреть там.Еще одно хорошее прочтение: Что должен знать каждый программист о кодировках .

. В настоящее время в C ++ существует только элементарная поддержка языка Unicode (например, char16_t и char32_t типов данных).и u8 / u / U буквенные префиксы).Поэтому выбор библиотеки для управления кодировками (особенно преобразованиями), безусловно, хороший совет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...