как конвертировать utf8 в std :: string? - PullRequest
2 голосов
/ 11 марта 2019

Я работаю над этим кодом, который получает ответ cpprest sdk, содержащий полезную нагрузку base64_encoded, которая является json. вот мой фрагмент кода:

typedef std::wstring string_t; //defined in basic_types.h in cpprest lib
    void demo() {
        http_response response; 
        //code to handle respose ...
        json::value output= response.extract_json();
        string_t payload = output.at(L"payload").as_string();
        vector<unsigned char> base64_encoded_payload = conversions::from_base64(payload);
        std::string utf8_payload(base64_encoded_payload.begin(), base64_encoded_payload.end()); //in debugger I see the Japanese chars are garbled.
        string_t utf16_payload = utf8_to_utf16(utf8_payload); //in debugger I see the Japanese chars are good here
        //then I need to process the utf8_payload which is an xml.
        //I have an API available to process the xml which takes an string
        processXML(utf16_payload); //need to convert utf16_payload to a string here;

    }

Я тоже попробовал это и вижу, что str содержит искаженные символы!

#include <codecvt>  // for codecvt_utf8_utf16
#include <locale>   // for wstring_convert
#include <string>   // for string, wstring
void wstr2str(void) {
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conversion;
    std::wstring japanese = L"北島 美奈";
    std::string str = conversion.to_bytes(japanese); //str is garbled:(
}

мои вопросы: можно ли преобразовать utf8, содержащий японский символ, в std :: string без искажений?

Обновление: Я получил доступ к коду processXML () и изменил тип входного аргумента на std :: wstring, и это сработало. Я понял, что когда создается XML, он конвертирует std :: string в wstring; однако, это не получалось хорошо!

void processXML(std::wstring xmlStrBuf) { //chaned xmlStrBuf to wstring and worked
// more code
CComBSTR xmlBuff = xmlStrBuf.c_str(); 
VARIANT_BOOL bSuccess = false;
xmlDoc->loadXML(xmlBuff, &bSuccess);
//more code

}

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

Ответы [ 2 ]

2 голосов
/ 11 марта 2019

Вы путаете различные понятия здесь.

Хранение

Так мы сохраняем / храним / храним наши данные. std::string - это набор char с, которые байтов . std::wstring - это набор wchar_t s, которые иногда имеют значение в 2 байта (но это не гарантируется!).

Кодирование

Вот что означают данные и как их следует интерпретировать. std::string, набор байтов, может содержать UTF-8, или UTF-16, или UTF-32, или ASCII, или ShiftJIS, или азбуку Морзе, или JPEG, или фильм, или мою ДНК (счастливая строка !).

В мире существуют сильные соглашения. Например, в Windows принято считать std::wstring для хранения UTF-16 (потому что для этого удобно двухбайтовое хранилище, а также потому, что так работает Windows API).

Более новые версии C ++ также дают нам такие вещи, как std::u16_string и std::u32_string, которые по-прежнему напрямую не имеют никакого понятия о кодировке, но предназначены для использования в UTF-16 и UTF -32 соответственно, потому что их имена делают это намерение более очевидным для читателей кода. В C ++ 20 будет введено std::u8_string, которое предназначено для обозначения строки в кодировке UTF-8 (в остальном более или менее похоже на std::string).

Но это всего лишь соглашений . Ничто в типе std::string не говорит «UTF-8» или что-либо еще. Он не знает, не заботится и не применяет какую-либо кодировку. Он просто хранит байты.

Итак, ваш вопрос о «преобразовании UTF-8 в std::string» на самом деле не имеет никакого смысла; это все равно что спрашивать, как превратить дорогу в машину.

«Что мне тогда делать?»

Ну, Base64 тоже не кодировка. Ну, на самом деле, это действительно так, но это кодировка поверх кодировки строк. Это способ передачи / экранирования / очистки необработанных байтов, а не способ описания того, как их интерпретировать позже. , запрашивающий cpprest преобразовать из Base64 , это просто преобразует способ предоставления необработанных байтов. Вот почему он дает вам std::vector<char>, а не std::string, потому что, хотя (как обсуждалось выше) std::string не заботится о кодировании, мы иногда используем std::vector<char>, чтобы действительно, правильно, полностью сказать, что "это У коллекции нет какой-либо конкретной кодировки, поэтому, пожалуйста, не пытайтесь угадать из соглашения или чего-либо еще, что такое кодировка в этом случае использования; все, что она знает, это то, что это набор байтов ". Это зависит от мнения. Некоторые люди все еще будут использовать std::string для этого; авторы cpprest решили не делать этого.

Суть в том, что использование функции from_base64 не может сказать нам ничего о кодировке текста, который вы получили. Для этого мы должны вернуться к документации для текста. У нас нет доступа к этому, и вы ничего не сказали нам об этом. Если бы это была просто строка JSON, кодировка была бы до библиотеки JSON cpprest, так что вы уже сделали бы. Однако это не так: это что-то, упакованное в представление Base64 тем, кто создал объект JSON. Опять же, эта информация не является чем-то, чем вы поделились с нами.

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

(Аналогично, во втором примере вы взяли a std::wstring, который [вероятно] уже хранит UTF-16 благодаря L"wide string literal", а затем сказали компьютеру, что это UTF-8 и преобразовать его «снова» в UTF-16, а затем извлечь необработанные байты в std::string. Ничего из этого не имеет смысла.)

Вместо этого, почему бы не буквально просто processXML(utf8_payload);?

Общие советы

Кодирование может быть довольно сложным, хотя с ним значительно легче справиться, как только вы сосредоточитесь на основных понятиях всех этих уровней абстракции.На будущее и для этого вопроса, если вы хотите уточнить это, вам нужно будет гарантировать, что вы абсолютно чисты на каждом этапе «конвейера» ваших данных, когда они передаются из места A в место B и получаютпреобразованный из типа C в тип D, и что бы то ни было, о том, какая кодировка должна быть на каждом из этих шагов.Если вы хотите изменить кодировку на одном из этих шагов, сделайте это (хотя это должно быть редко!).Но прежде чем писать какой-либо код, убедитесь, что вы точно знаете, что именно вам нужно, в противном случае вы попадете в огромный клубок.

В конце концов вы начнете обнаруживать шаблоны, которые могут помочь, хотя,Например, если вы ожидали некоторого восхитительного вывода не-ASCII и вместо этого увидели странный текст с большим количеством символов "Å" , это, вероятно, UTF-8, который по ошибке интерпретируется как ASCII.Это связано с тем, что специальная последовательность, обозначающая кодовые точки Unicode, большие, чем один байт в UTF-8, часто начинается с байта, числовое значение которого совпадает со значением буквы "Å" в ASCII (хорошо,ИСО / МЭК 8859, но достаточно близко).

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

Буквально на прошлой неделе последний пример сэкономил мне немного времени: я сразу понял, чтомои исходные данные должны были быть в формате UTF-8, и поэтому я смог быстро принять решение удалить байт-копию в std::wstring, который я пытался.Изучение байтов кодирующим способом не выявило паттерна "Å" , и тогда это было так.Это было важно, потому что у меня не было документации для источника данных и, следовательно, я не мог просто посмотреть, какой была кодировка .Я должен был угадать / сделать вывод.Надеюсь, это не будет иметь место для вас здесь.

0 голосов
/ 11 марта 2019

std::string - это просто контейнер для 8-битной ширины char, и он не знает / не заботится о кодировке.Всегда думайте в символах (буквы, цифры, знаки препинания и т. Д.) Первые 128 символов (0-127) были определены в соответствии со стандартом ASCII, поэтому для хранения каждого символа требуется один char.При наличии всех языков и символов мы не могли бы представить каждый из них с 256 возможностями.Кодировка UTF-8 представляет способ решения этой проблемы, позволяя одному символу занимать 1, 2, 3 или 4 char в ширину.Но для объекта std::string это полностью прозрачно, и он все еще имеет дело с серией символов.

Причина, по которой вы думаете, что строка искажена, вероятно, потому, что ваш отладчик принимает содержимоеstd::string это всегда 1 символ на символ (например, расширенный ASCII), и поэтому он отображает неправильные символы.

Редактировать: вы можете прочитать этот пост также.

...