#c #c 17 #utf #std-filesystem
Вопрос:
Начиная с C 11, можно преобразовать UTF8 в UTF16 wchar_t
(по крайней мере, в Windows, где wchar_t
ширина 16 бит) с помощью std::codecvt_utf8_utf16
:
std::wstring utf8ToWide( const char* utf8 )
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes( utf8 );
}
К сожалению, в C 17 std::codecvt_utf8_utf16
это устарело. Но внутри есть std::filesystem::path
все возможные преобразования, например, в нем есть члены
std::string string() const;
std::wstring wstring() const;
std::u8string u8string() const;
std::u16string u16string() const;
std::u32string u32string() const;
Таким образом, приведенную выше функцию можно переписать следующим образом:
std::wstring utf8ToWide( const char* utf8 )
{
return std::filesystem::path( (const char8_t*) utf8 ).wstring();
}
И в отличие std::codecvt_utf8_utf16
от этого не будет использовать какой-либо устаревший фрагмент C .
Каких недостатков можно ожидать от такого конвертера? Например, путь не может быть длиннее определенной длины или там запрещены определенные символы Юникода?
Комментарии:
1. Строковыми типами UTF16 являются u16string и chr16_t, а не wstring и wchar_t. То же самое для UTF8.
char
может быть в любой кодировке. Если вы проверите документацию по этим методам , вы увидите, что все они либо не определены, либо зависят от системы. Я подозреваю, что реализация использует любой метод, предоставляемый ОС, точно так же, как люди делали это раньшеcodecvt_utf8_utf16
, был устаревшим, но не удаленным. Для этого нет хорошего решения2. В документации также проводится различие между
char
иchar8_t
.char8_t
всегда обрабатывается (или должно обрабатываться) как UTF8char
. кодировка зависит от настроек среды3. @PanagiotisKanavos
There's no good solution to this
Хотя и нет хорошего стандартного решения, я бы сказал, что есть хорошее решение: используйте библиотеку.4. Преобразование UTF8 в UTF16 с использованием std::файловой системы::путь Ко мне, это звучит как злоупотребление функцией для чего-то другого. На самом деле, я все еще верю, что преобразование UTF-16 <-> UTF-8 может быть достигнуто с помощью довольно простой битовой арифметики (и это часть концепции). Я бы предпочел функцию «ручной вязки» (и тщательно протестированную) по сравнению с устаревшей
codecvt_utf8_utf16()
, которую я даже использовал до того, как последняя стала доступной. (По крайней мере, до тех пор, пока кто-нибудь не назовет мне вескую причину, по которой я не должен этого делать.)5. @PanagiotisKanavos: » Метод ручной работы будет отличаться от методов операционной системы в крайних случаях, что приведет к неприятным сюрпризам для разработчиков и конечных пользователей». … нет, это не так. UTF-8 и UTF-16 являются довольно простыми числовыми преобразованиями, и они четко определены для всего 21-разрядного диапазона Юникода. Это не сложный, сложный код для написания. Для реализации таких вещей приличному программисту потребовалось бы не более 4 часов, и вы могли бы добавить некоторое время для тестирования конкретных случаев.
Ответ №1:
Каких недостатков можно ожидать от такого конвертера?
Что ж, давайте избавимся от самого очевидного недостатка. Для пользователя, который не знает, что вы делаете, это не имеет смысла. Выполнение преобразования UTF-8 в 16 с использованием типа пути-это безумие, и его следует немедленно рассматривать как запах кода. Это тот ужасный взлом, который вы делаете, когда без необходимости отказываетесь просто загрузить простую библиотеку, которая сделала бы это правильно.
Кроме того, это не обязательно должно сработать. path
предназначен для хранения… пути. Отсюда и название. В частности, они предназначены для хранения путей таким образом, чтобы их легко могла использовать рассматриваемая файловая система. Таким образом, строка, хранящаяся в a path
, может иметь любые ограничения, которые хочет наложить на нее файловая система, за исключением небольшого количества вещей, которые требует от нее стандарт C .
Например, если файловая система не учитывает регистр (или даже просто не учитывает регистр ASCII), законной реализацией является преобразование всех строк в регистр в нижнем регистре, когда они хранятся в a path
. Или для преобразования в регистр, когда вы извлекаете их из a path
. Или что — нибудь в этом роде.
path
может конвертировать все ваши
s в /
s. Или твои :
» ы » в /
«ы». Или любые другие зависящие от реализации трюки, которые он хочет выполнить.
Если вы боитесь использовать устаревшее средство, просто скачайте простую библиотеку преобразования UTF-8/16. Или напишите его сами; это не так уж сложно.
Комментарии:
1. путь может преобразовать все ваши в /с. Или ваш :s в /’s. На самом деле я думал, что только явный вызов
std::filesystem::path::make_preferred
может это сделать.