Преобразование между строкой, u16string и u32string
Я искал способ преобразования между строковыми типами Unicode и наткнулся на этот метод. Мало того, что я не полностью понимаю метод (нет комментариев), но также статья подразумевает, что в будущем будут лучшие методы.
Если это лучший метод, не могли бы вы указать, что заставляет его работать, а если нет, то я хотел бы услышать предложения по лучшим методам.
3 ответа
mbstowcs()
а также wcstombs()
не обязательно конвертировать в UTF-16 или UTF-32, они конвертируются в wchar_t
и независимо от локали wchar_t
кодировка есть. Все локали Windows используют двухбайтовые wchar_t
и UTF-16 в качестве кодировки, но другие основные платформы используют 4-байтовый wchar_t
с UTF-32 (или даже не кодировкой Unicode для некоторых локалей). Платформа, которая поддерживает только однобайтовые кодировки, может даже иметь один байт wchar_t
и кодировка отличается в зависимости от локали. Так wchar_t
Мне кажется, это плохой выбор для переносимости и Unicode. *
Некоторые лучшие варианты были введены в C++11; новые специализации std::codecvt, новые классы codecvt и новый шаблон, чтобы сделать их использование для преобразований очень удобным.
Сначала новый шаблонный класс для использования codecvt - это std::wstring_convert. Создав экземпляр класса std:: wstring_convert, вы можете легко преобразовывать строки:
std::wstring_convert<...> convert; // ... filled in with a codecvt to do UTF-8 <-> UTF-16
std::string utf8_string = u8"This string has UTF-8 content";
std::u16string utf16_string = convert.from_bytes(utf8_string);
std::string another_utf8_string = convert.to_bytes(utf16_string);
Чтобы сделать другое преобразование, вам просто нужны разные параметры шаблона, одним из которых является фасет codecvt. Вот некоторые новые аспекты, которые легко использовать с wstring_convert:
std::codecvt_utf8_utf16<char16_t> // converts between UTF-8 <-> UTF-16
std::codecvt_utf8<char32_t> // converts between UTF-8 <-> UTF-32
std::codecvt_utf8<char16_t> // converts between UTF-8 <-> UCS-2 (warning, not UTF-16! Don't bother using this one)
Примеры их использования:
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::string a = convert.to_bytes(u"This string has UTF-16 content");
std::u16string b = convert.from_bytes(u8"blah blah blah");
Новые специализации std:: codecvt немного сложнее использовать, потому что они имеют защищенный деструктор. Чтобы обойти это, вы можете определить подкласс, который имеет деструктор, или вы можете использовать шаблонную функцию std::use_facet, чтобы получить существующий экземпляр codecvt. Кроме того, проблема с этими специализациями заключается в том, что вы не можете использовать их в Visual Studio 2010, поскольку специализация шаблонов не работает с типами typedef, и этот компилятор определяет char16_t и char32_t как typedefs. Вот пример определения вашего собственного подкласса codecvt:
template <class internT, class externT, class stateT>
struct codecvt : std::codecvt<internT,externT,stateT>
{ ~codecvt(){} };
std::wstring_convert<codecvt<char16_t,char,std::mbstate_t>,char16_t> convert16;
std::wstring_convert<codecvt<char32_t,char,std::mbstate_t>,char32_t> convert32;
Специализация char16_t конвертирует между UTF-16 и UTF-8. Специализация char32_t, UTF-32 и UTF-8.
Обратите внимание, что эти новые преобразования, предоставляемые C++ 11, не включают какой-либо способ прямого преобразования между UTF-32 и UTF-16. Вместо этого вам просто нужно объединить два экземпляра std::wstring_convert.
***** Я решил добавить примечание к wchar_t и его назначению, чтобы подчеркнуть, почему его вообще не следует использовать для Unicode или переносимого интернационализированного кода. Ниже приведена краткая версия моего ответа /questions/18576857/chto-ne-tak-s-c-wchart-i-wstrings-kakie-est-alternativyi-shirokim-simvolam/18576878#18576878
Что такое wchar_t?
wchar_t определено так, что кодировка char любой локали может быть преобразована в wchar_t, где каждый wchar_t представляет ровно одну кодовую точку:
Тип wchar_t - это отдельный тип, значения которого могут представлять разные коды для всех членов наибольшего расширенного набора символов, указанного среди поддерживаемых локалей (22.3.1). - [basic.fundamental] 3.9.1/5
Это не требует, чтобы wchar_t был достаточно большим, чтобы представлять любой символ из всех локалей одновременно. То есть кодировка, используемая для wchar_t, может отличаться в разных локалях. Это означает, что вы не можете обязательно преобразовать строку в wchar_t, используя одну локаль, а затем преобразовать обратно в char, используя другую локаль.
Поскольку на практике это является основным использованием wchar_t, вы можете спросить, для чего он нужен, если не для этого.
Первоначальная цель и цель wchar_t состояла в том, чтобы сделать обработку текста простой, определив ее так, чтобы она требовала однозначного сопоставления кодовых единиц строки с символами текста, что позволяет использовать те же простые алгоритмы, что и строки ascii. работать с другими языками.
К сожалению, требования для wchar_t предполагают взаимно-однозначное сопоставление символов и кодовых точек для достижения этой цели. Unicode нарушает это предположение, поэтому вы не можете безопасно использовать wchar_t для простых текстовых алгоритмов.
Это означает, что переносимое программное обеспечение не может использовать wchar_t ни в качестве общего представления для текста между локалями, ни для обеспечения возможности использования простых текстовых алгоритмов.
Какая польза от wchar_t сегодня?
Не так много, для переносимого кода в любом случае. Если __STDC_ISO_10646__
определяется, тогда значения wchar_t напрямую представляют кодовые точки Unicode с одинаковыми значениями во всех локалях. Это делает безопасным выполнение преобразований между локалями, упомянутых ранее. Однако вы не можете полагаться только на него, чтобы решить, что вы можете использовать wchar_t таким образом, потому что, хотя большинство платформ Unix определяют его, Windows не делает этого, хотя Windows использует один и тот же языковой стандарт wchar_t во всех языковых стандартах.
Причина, по которой Windows не определяет __STDC_ISO_10646__
Я думаю, потому что Windows использует UTF-16 в качестве кодировки wchar_t, и потому что UTF-16 использует суррогатные пары для представления кодовых точек, больших, чем U+FFFF, что означает, что UTF-16 не удовлетворяет требованиям для __STDC_ISO_10646__
,
Для конкретного кода платформы wchar_t может быть более полезным. Это по сути требуется в Windows (например, некоторые файлы просто не могут быть открыты без использования имен файлов wchar_t), хотя, насколько я знаю, Windows является единственной платформой, где это верно (поэтому, возможно, мы можем думать о wchar_t как о Windows_char_t).
В ретроспективе wchar_t явно не полезен для упрощения обработки текста или для хранения независимого от локали текста. Переносимый код не должен пытаться использовать его для этих целей.
Я написал вспомогательные функции для преобразования в / из строк UTF8 (C++11):
#include <string>
#include <locale>
#include <codecvt>
using namespace std;
template <typename T>
string toUTF8(const basic_string<T, char_traits<T>, allocator<T>>& source)
{
string result;
wstring_convert<codecvt_utf8_utf16<T>, T> convertor;
result = convertor.to_bytes(source);
return result;
}
template <typename T>
void fromUTF8(const string& source, basic_string<T, char_traits<T>, allocator<T>>& result)
{
wstring_convert<codecvt_utf8_utf16<T>, T> convertor;
result = convertor.from_bytes(source);
}
Пример использования:
// Unicode <-> UTF8
{
wstring uStr = L"Unicode string";
string str = toUTF8(uStr);
wstring after;
fromUTF8(str, after);
assert(uStr == after);
}
// UTF16 <-> UTF8
{
u16string uStr;
uStr.push_back('A');
string str = toUTF8(uStr);
u16string after;
fromUTF8(str, after);
assert(uStr == after);
}
Насколько я знаю, C++ не предоставляет стандартных методов для преобразования из или в UTF-32. Однако для UTF-16 существуют методы mbstowcs (многобайтовая и широкая символьная строка) и обратные wcstombs.
Если вам также нужен UTF-32, вам нужен iconv, который находится в POSIX 2001, но не в стандарте C, поэтому в Windows вам понадобится замена, например libiconv.
Вот пример того, как использовать mbstowcs:
#include <string>
#include <iostream>
#include <stdlib.h>
using namespace std;
wstring widestring(const string &text);
int main()
{
string text;
cout << "Enter something: ";
cin >> text;
wcout << L"You entered " << widestring(text) << ".\n";
return 0;
}
wstring widestring(const string &text)
{
wstring result;
result.resize(text.length());
mbstowcs(&result[0], &text[0], text.length());
return result;
}
Обратное происходит так:
string mbstring(const wstring &text)
{
string result;
result.resize(text.length());
wcstombs(&result[0], &text[0], text.length());
return result;
}
Nitpick: Да, я знаю, размер wchar_t определяется реализацией, поэтому он может быть 4 байта (UTF-32). Тем не менее, я не знаю компилятор, который делает это.