Я искал способ преобразования между строковыми типами Unicode и наткнулся на этот метод. Я не только не до конца понимаю метод (нет комментариев), но еще и статья подразумевает, что в будущем будут лучшие методы.

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

46
DrYap 29 Авг 2011 в 20:09

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'd, и этот компилятор определяет 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 или переносимого интернационализированного кода. Ниже приводится краткая версия моего ответа https://stackoverflow.com/a/11107667/365496.

Что такое wchar_t?

Wchar_t определяется таким образом, что любая кодировка символов языкового стандарта может быть преобразована в 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 бесполезен для упрощения обработки текста или в качестве хранилища для текста, не зависящего от языкового стандарта. Переносимый код не должен пытаться использовать его для этих целей.

91
Community 23 Май 2017 в 12:10

Я написал вспомогательные функции для преобразования в / из строк 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);
}
14
Dmytro 16 Янв 2016 в 13:24

Насколько мне известно, 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). Однако я не знаю компилятора, который бы это делал.

-2
Raphael R. 29 Авг 2011 в 16:31