char против wchar_t, когда использовать какой тип данных
Я хочу понять разницу между char
а также wchar_t
? Я это понимаю wchar_t
использует больше байтов, но могу ли я получить четкий пример, чтобы различать, когда я буду использовать char
против wchar_t
3 ответа
Принципиально, использовать wchar_t
когда кодировка имеет больше символов, чем char
может содержать.
Фонchar
Тип имеет достаточную емкость для хранения любого символа (кодировки) в наборе символов ASCII.
Проблема в том, что для многих языков требуется больше кодировок, чем для ASCII. Таким образом, вместо 127 возможных кодировок требуется больше. Некоторые языки имеют более 256 возможных кодировок. char
Тип не гарантирует диапазон больше 256. Таким образом, требуется новый тип данных.
wchar_t
, иначе известные символы, предоставляет больше места для кодировок.
Резюме
использование char
тип данных, когда диапазон кодировок составляет 256 или менее, например, ASCII. использование wchar_t
когда вам нужна емкость более 256.
Предпочитаю Unicode для обработки больших наборов символов (например, смайликов).
Короткий ответ:
Вы никогда не должны использовать wchar_t
в современном C++, за исключением случаев взаимодействия с OS-специфическими API (в основном используют wchar_t
только для вызова функций Windows API).
Длинный ответ:
Проектирование стандартной библиотеки C++ подразумевает, что есть только один способ обработки Unicode - хранение строк в кодировке UTF-8 в массивах char, так как почти все функции существуют только в вариантах char (подумайте о std::exception::what
).
В программе на C++ у вас есть две локали:
- Стандартная локаль библиотеки C, установленная std::setlocale
- Стандартная локаль библиотеки C++, установленная std::locale::global
К сожалению, ни одна из них не определяет поведение стандартных функций, которые открывают файлы (например, std::fopen
, std::fstream::open
так далее). Поведение в разных ОС различается:
- Linux не зависит от кодировки, поэтому эти функции просто передают символьную строку в базовый системный вызов. - В Windows символьная строка преобразуется в широкую строку с использованием локали, определенной пользователем, перед выполнением системного вызова.
В Linux все обычно работает нормально, поскольку все используют локали на основе UTF-8, поэтому весь пользовательский ввод и аргументы передаются main
функции будут в кодировке UTF-8. Но, возможно, вам все равно придется явно переключать текущие локали на варианты UTF-8, так как по умолчанию программа C++ запускается по умолчанию "C"
локали. На данный момент, если вы заботитесь только о Linux и не нуждаетесь в поддержке Windows, вы можете использовать массивы символов и std::string
при условии, что это последовательности UTF-8, и все "просто работает".
Проблемы возникают, когда вы хотите поддерживать Windows, так как здесь у вас всегда есть дополнительный 3-й языковой стандарт: тот, который установлен для текущего пользователя, который можно настроить где-то в "Панели управления". Основная проблема заключается в том, что эта локаль никогда не является юникодной, поэтому невозможно использовать такие функции, как std::fopen(const char *)
а также std::fstream::open(const char *)
открыть файл, используя путь Unicode. В Windows вам придется использовать пользовательские оболочки, которые используют нестандартные функции Windows, такие как _wfopen
, std::fstream::open(const wchar_t *)
на винде. Вы можете проверить Boost.Nowide (еще не включен в Boost), чтобы увидеть, как это можно сделать: http://cppcms.com/files/nowide/html/
С C++ 17 вы можете использовать std::filesystem::path
хранить путь к файлу переносимым способом, но он все еще не работает в Windows:
- Неявный конструктор
std::filesystem::path::path(const char *)
использует пользовательский языковой стандарт в MSVC, и нет способа заставить его использовать UTF-8. функцияstd::filesystem::u8string
следует использовать для построения пути из строки UTF-8, но слишком легко забыть об этом и использовать вместо этого неявную конструкцию. std::error_category::message(int)
для обеих категорий ошибок возвращает описание ошибки с использованием пользовательской кодировки.
Итак, что мы имеем в Windows:
- Стандартные библиотечные функции, которые открывают файлы, не работают и никогда не должны использоваться.
- Аргументы переданы
main(int, char**)
сломаны и никогда не должны использоваться. - Функции WinAPI, заканчивающиеся * A, и макросы не работают и никогда не должны использоваться.
std::filesystem::path
частично сломан и никогда не должен использоваться напрямую.- Категории ошибок, возвращаемые
std::generic_category
а такжеstd::system_category
сломаны и никогда не должны использоваться.
Если вам нужно долгосрочное решение для нетривиального проекта, я бы порекомендовал:
- Использование Boost.Nowide или непосредственная реализация аналогичного функционала - это исправленная неработающая стандартная библиотека.
- Реализация стандартных категорий ошибок, возвращаемых
std::generic_category
а такжеstd::system_category
чтобы они всегда возвращали строки в кодировке UTF-8. - Упаковка
std::filesystem::path
так что новый класс всегда будет использовать UTF-8 при преобразовании пути в строку и строки в путь. - Оборачивание всех необходимых функций из
std::filesystem
чтобы они использовали вашу обертку пути и ваши категории ошибок.
К сожалению, это не решит проблемы с другими библиотеками, которые работают с файлами, но 99% из них все равно сломаны (не поддерживают Unicode).
Такова жизнь программиста на C++. Microsoft могла бы исправить это, позволив нам переключать среду выполнения Windows на локаль на основе UTF-8, но этого не произошло из-за обратной совместимости.
Вы можете проверить эту ссылку для дальнейшего объяснения: http://utf8everywhere.org/
Никогда не используйте wchar_t
,
По возможности используйте (какой-то массив) char
, такие как std::string
и убедитесь, что он закодирован в UTF-8.
Когда вы должны взаимодействовать с API, которые не говорят на UTF-8, используйте char16_t
или же char32_t
, Никогда не используйте их иначе; они дают только иллюзорные преимущества и поощряют ошибочный код.
Обратите внимание, что существует множество случаев, когда более одного char32_t
требуется для представления одного видимого пользователю символа. OTOH, используя UTF-8 с char
заставляет вас обращаться с переменной шириной очень рано.