Что именно может представлять wchar_t?

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

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

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

Тип wchar_t - это отдельный тип, значения которого могут представлять различные коды для всех членов самого большого расширенного набора символов, указанного среди поддерживаемых локалей. Тип wchar_t должен иметь те же требования к размеру, подписи и выравниванию, что и один из других интегральных типов, называемый его базовым типом. Типы char16_t и char32_t обозначают разные типы с одинаковым размером, подписью и выравниванием как uint_least16_t и uint_least32_t, соответственно, в <cstdint>, называемые базовыми типами.

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

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

Ответ 1

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

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

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

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

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

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

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

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

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

Ответ 2

wchar_t используется в Windows, который использует формат UTF16-LE. wchar_t требуются широкие функции char. Например, 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 не используются для представления других кодовых точек Юникода. Это включает в себя escape-последовательность '\', printf формата 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 везде".

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

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

Программист Windows, как правило, использует UTF8 для сохранения файлов, веб-страниц и т.д. Так что меньше беспокоиться о не-Windows-программистах с точки зрения совместимости.

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

Ответ 3

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

Это зависит от того, с какой кодировкой вы имеете дело. В случае UTF- 8 вы просто в порядке с char и std :: string. UTF- 8 означает, что младший блок кодирования составляет 8 бит: все кодовые номера Юникода от 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 и построения с настройкой "Юникод" вы получите UTF- 16: TCHAR и CString будут основаны на wchar_t вместо char.

Ответ 4

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

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

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

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

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

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

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

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

Ответ 5

Все зависит от того, что вы подразумеваете под "сделкой", но одно можно сказать наверняка: в случае 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 в/из родных Windows API, то вы можете добавить методы для преобразования в и из const WCHAR * (которые снова вы будете реализовывать так, чтобы они работали для всех значений mychar_t). Или вы можете пойти дальше и абстрагироваться от некоторой или всей поддержки Unicode, предоставляемой платформой и библиотекой, которую вы используете. Mac, например, имеет богатую поддержку Unicode, но он доступен только для Objective-C, поэтому вы должны его обернуть. Это зависит от того, насколько портативен ваш код.

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