Преобразование UTF8 в UTF16 с использованием std::файловая система::путь

#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 всегда обрабатывается (или должно обрабатываться) как UTF8 char . кодировка зависит от настроек среды

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 может это сделать.