Преобразование std::string в Unicode в Linux

#c #linux #unicode

#c #linux #unicode

Вопрос:

РЕДАКТИРОВАТЬ Я изменил вопрос, поняв, что с самого начала это было неправильно.

Я переношу часть приложения C # на Linux, где мне нужно получить байты строки UTF-16:

 string myString = "ABC";
byte[] bytes = Encoding.Unicode.GetBytes(myString);
  

Так что bytes массив теперь:

 "65 00 66 00 67 00" (bytes)
  

Как я могу добиться того же в C в Linux? У меня myString определено как std::string , и кажется, что std::wstring в Linux это 4 байта?

Комментарии:

1. Ваше преобразование выполняется в UTF-16, ваш пример предполагает, что вы хотите UTF-16LE, но из чего вы конвертируете? (US-ASCII, UTF-8, ISO-8859-*) и выбрали ли вы библиотеку unicode для использования?

2. Я конвертирую из стандартной (UTF-16?) строки .NET. Кроме того (и извините, мне не хватает знаний в этой области), я не использую никаких других библиотек, кроме стандартных, какую из них мне следует использовать? 🙂

3. Я «вроде» взломал это, объявив массив в два раза больше моей строки и просто установив каждый символ [i * 2], но это последнее, чего я хочу 🙂

4. @hmemcpy: В таком случае ваш вопрос вводит в заблуждение, потому что строка не «65 66 67» (байты) для начала. Я думаю, вам нужно предоставить больше контекста C для вашего вопроса, поскольку вы ищете ответ на C . Если строка уже находится в нужной вам кодировке, вам просто нужно прочитать байты строки по одному за раз.

5. @Charles Ты прав, конечно! Перечитав вопрос, я понимаю, что теперь это неверно — the . ЧИСТАЯ строка уже имеет формат UTF-16, что означает 2 байта на каждый символ. Все, что мне нужно, это, на самом деле, реализация getBytes… Я изменю свой вопрос

Ответ №1:

Ваш вопрос не совсем ясен, но я попытаюсь устранить некоторую путаницу.

Введение

Состояние обработки набора символов в C (и который был унаследован C ) после поправки 95 года к стандарту C.

  • используемый набор символов задается текущим языком

  • wchar_t предназначен для хранения кодовой точки

  • char предназначен для хранения многобайтовой кодированной формы (ограничение, например, заключается в том, что символы в базовом наборе символов должны быть закодированы в один байт)

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

Таким образом, при 16 битах wchar_t вы ограничены BMP. Использование заменителей UTF-16 несовместимо, но я думаю, MS и IBM более или менее вынуждены это делать, потому что они верили Unicode, когда говорили, что они навсегда останутся 16-битной кодировкой. Те, кто отложил поддержку Unicode, как правило, используют 32-битный wchar_t.

Новые стандарты не сильно меняются. В основном существуют литералы для строк в кодировках UTF-8, UTF-16 и UTF-32, а также типы для 16-битного и 32-битного символов. В стандартных библиотеках практически отсутствует дополнительная поддержка Unicode.

Как выполнить преобразование одной кодировки в другую

Вы должны быть в языковом стандарте, который использует Unicode. Надеюсь

 std::locale::global(locale(""));
  

для этого будет достаточно. В противном случае ваша среда настроена неправильно (или настроена для другой кодировки и предполагает, что Unicode не будет сервисом для вашего пользователя.).

Стиль C

Используйте функции wcstomsb и mbstowcs . Вот пример того, что вы просили.

 std::string narrow(std::wstring constamp; s)
{
    std::vector<char> result(4*s.size()   1);
    size_t used = wcstomsb(amp;result[0], s.data(), result.size());
    assert(used < result.size());
    return result.data();
}
  

Стиль C

Аспект codecvt локали обеспечивает необходимую функциональность. Преимущество заключается в том, что вам не нужно изменять глобальную локаль для его использования. Неудобство заключается в том, что использование более сложное.

 #include <locale>
#include <iostream>
#include <string>
#include <vector>
#include <assert.h>
#include <iomanip>

std::string narrow(std::wstring constamp; s,
                   std::locale loc = std::locale())
{
    std::vector<char> result(4*s.size()   1);
    wchar_t const* fromNext;
    char* toNext;
    mbstate_t state = {0};
    std::codecvt_base::result convResult
        = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t> >(loc)
        .out(state,amp;s[0], amp;s[s.size()], fromNext,
             amp;result[0], amp;result[result.size()], toNext);

    assert(fromNext == amp;s[s.size()]);
    assert(toNext != amp;result[result.size()]);
    assert(convResult == std::codecvt_base::ok);
    *toNext = '';

    return amp;result[0];
}

std::wstring widen(std::string constamp; s,
                   std::locale loc = std::locale())
{
    std::vector<wchar_t> result(s.size()   1);
    char const* fromNext;
    wchar_t* toNext;
    mbstate_t state = {0};
    std::codecvt_base::result convResult
        = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t> >(loc)
        .in(state, amp;s[0], amp;s[s.size()], fromNext,
            amp;result[0], amp;result[result.size()], toNext);

    assert(fromNext == amp;s[s.size()]);
    assert(toNext != amp;result[result.size()]);
    assert(convResult == std::codecvt_base::ok);
    *toNext = L'';

    return amp;result[0];
}
  

вам следует заменить утверждения лучшей обработкой.

Кстати, это стандартный C и не предполагает использование Unicode, за исключением вычисления размера результата, вы можете сделать лучше, проверив convResult, который может указывать на частичное преобразование).

Комментарии:

1. Ваша narrow() функция в стиле C утверждает, что количество символов было «достаточным», но она не усекает строку.

2. Кроме того, в некоторых формах нормализации один символ может занимать до 6 байт .

3. опечатка: wchat_t ; пытался избавить вас от хлопот, отредактировав его сам, но они не допускают правки из 1 буквы 🙂

4. Если кодировка входной строки известна, то, технически говоря, я не «должен быть в локали». Должен ли я?

Ответ №2:

Самый простой способ — взять небольшую библиотеку, такую как UTF8 CPP, и сделать что-то вроде:

 utf8::utf8to16(line.begin(), line.end(), back_inserter(utf16line));
  

Ответ №3:

Обычно я использую класс UnicodeConverter из библиотек Poco C . Если вам не нужна зависимость, вы можете взглянуть на код.