UTF8 к/от широкому символьному преобразованию в STL

69
задан Vladimir Grigorov 13 December 2017 в 12:46
поделиться

6 ответов

25
ответ дан Nemanja Trifunovic 24 November 2019 в 13:49
поделиться

Проблемное определение явно указывает, что 8-разрядная кодировка символов является UTF-8. Это делает это тривиальной проблемой; все, чего требуется, является небольшим битовым жонглированием для преобразования от одной спецификации UTF до другого.

Только посмотрели на кодировку на этих страницах Wikipedia для UTF-8, UTF-16, и UTF-32.

принцип прост - проходят вход и собирают 32-разрядную кодовую точку Unicode согласно одной спецификации UTF, затем испускают кодовую точку согласно другой спецификации. Для отдельных кодовых точек не нужен никакой перевод, как требовался бы с любой другой кодировкой символов; это - то, что делает это простой проблемой.

Вот быстрая реализация wchar_t к преобразованию UTF-8 и наоборот. Это предполагает, что вход уже правильно кодируется - старая поговорка "Мусор в, мусор" применяется здесь. Я полагаю, что проверка кодирования лучше всего сделана как отдельный шаг.

std::string wchar_to_UTF8(const wchar_t * in)
{
    std::string out;
    unsigned int codepoint = 0;
    for (in;  *in != 0;  ++in)
    {
        if (*in >= 0xd800 && *in <= 0xdbff)
            codepoint = ((*in - 0xd800) << 10) + 0x10000;
        else
        {
            if (*in >= 0xdc00 && *in <= 0xdfff)
                codepoint |= *in - 0xdc00;
            else
                codepoint = *in;

            if (codepoint <= 0x7f)
                out.append(1, static_cast<char>(codepoint));
            else if (codepoint <= 0x7ff)
            {
                out.append(1, static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f)));
                out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
            }
            else if (codepoint <= 0xffff)
            {
                out.append(1, static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f)));
                out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
                out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
            }
            else
            {
                out.append(1, static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07)));
                out.append(1, static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)));
                out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
                out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
            }
            codepoint = 0;
        }
    }
    return out;
}

вышеупомянутый код работает и на UTF-16 и на вход UTF-32, просто потому что диапазон d800 до dfff является недопустимыми кодовыми точками; они указывают, что Вы декодируете UTF-16. Если Вы знаете, что wchar_t 32 бита тогда, Вы могли удалить некоторый код для оптимизации функции.

std::wstring UTF8_to_wchar(const char * in)
{
    std::wstring out;
    unsigned int codepoint;
    while (*in != 0)
    {
        unsigned char ch = static_cast<unsigned char>(*in);
        if (ch <= 0x7f)
            codepoint = ch;
        else if (ch <= 0xbf)
            codepoint = (codepoint << 6) | (ch & 0x3f);
        else if (ch <= 0xdf)
            codepoint = ch & 0x1f;
        else if (ch <= 0xef)
            codepoint = ch & 0x0f;
        else
            codepoint = ch & 0x07;
        ++in;
        if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff))
        {
            if (sizeof(wchar_t) > 2)
                out.append(1, static_cast<wchar_t>(codepoint));
            else if (codepoint > 0xffff)
            {
                out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10)));
                out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff)));
            }
            else if (codepoint < 0xd800 || codepoint >= 0xe000)
                out.append(1, static_cast<wchar_t>(codepoint));
        }
    }
    return out;
}

Снова, если Вы знаете, что wchar_t 32 бита, Вы могли удалить некоторый код из этой функции, но в этом случае это не должно иметь никакого значения. Выражение sizeof(wchar_t) > 2 известно во время компиляции, таким образом, любой достойный компилятор распознает мертвый код и удалит его.

17
ответ дан Mark Ransom 24 November 2019 в 13:49
поделиться

Можно извлечь utf8_codecvt_facet от библиотека сериализации Повышения .

Их пример использования:

  typedef wchar_t ucs4_t;

  std::locale old_locale;
  std::locale utf8_locale(old_locale,new utf8_codecvt_facet<ucs4_t>);

  // Set a New global locale
  std::locale::global(utf8_locale);

  // Send the UCS-4 data out, converting to UTF-8
  {
    std::wofstream ofs("data.ucd");
    ofs.imbue(utf8_locale);
    std::copy(ucs4_data.begin(),ucs4_data.end(),
          std::ostream_iterator<ucs4_t,ucs4_t>(ofs));
  }

  // Read the UTF-8 data back in, converting to UCS-4 on the way in
  std::vector<ucs4_t> from_file;
  {
    std::wifstream ifs("data.ucd");
    ifs.imbue(utf8_locale);
    ucs4_t item = 0;
    while (ifs >> item) from_file.push_back(item);
  }

Ищут utf8_codecvt_facet.hpp и utf8_codecvt_facet.cpp файлы в источниках повышения.

23
ответ дан Constantin 24 November 2019 в 13:49
поделиться

Существует несколько способов сделать это, но результаты зависят от того, что кодировки символов находятся в string и wstring переменные.

, Если Вы знаете эти string, ASCII, можно просто использовать wstring конструктор итератора:

string s = "This is surely ASCII.";
wstring w(s.begin(), s.end());

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

, Если Ваш string содержит символы в кодовой странице, то может $DEITY щадить Вашу душу.

12
ответ дан Ben Straub 24 November 2019 в 13:49
поделиться

Можно использовать codecvt фасет локали . Существует определенная определенная специализация, codecvt<wchar_t, char, mbstate_t>, который может быть полезен для Вас, хотя, поведение этого является определенным для системы, и не гарантирует преобразование в UTF-8 всегда.

2
ответ дан Chris Jester-Young 24 November 2019 в 13:49
поделиться

Я не думаю, что существует портативный способ сделать это. C++ не знает кодирование своих многобайтовых символов.

Как предложенный Chris, Ваш лучший выбор состоит в том, чтобы играть с codecvt.

-1
ответ дан Martin Cote 24 November 2019 в 13:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: