Как конвертировать utf8 в std::string?
Я работаю над этим кодом, который получает ответ 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 ответа
Вы путаете разные понятия здесь.
Место хранения
Вот как мы сохраняем / храним / храним наши данные. std::string
это коллекция char
s, которые являются байтами. 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, что скорее противоположно тому, что вы описали, что вы хотели сделать.
(Точно так же во втором примере вы взяли 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 (ну, ISO/IEC 8859, но достаточно близко).
Точно так же, если вы получаете японский язык и не ожидаете этого, по моему опыту, это обычно потому, что вы дали компьютеру несколько байтов и сказали, что они являются строкой в кодировке UTF-16, тогда как на самом деле они были UTF-8. Вы просто становитесь более опытными в распознавании этих шаблонов по мере того, как вы больше работаете, и это может помочь вам быстрее исправить ошибки.
Буквально на прошлой неделе последний пример сэкономил мне немало времени: я сразу понял, что мои исходные данные должны быть в формате UTF-8, и поэтому смог быстро принять решение об удалении байт-копии в std::wstring
что я пытался Изучение байтов в кодирующе-независимом виде также выявило паттерн "А", и тогда это было именно так. Это было важно, потому что у меня не было документации для источника данных, и поэтому я не мог просто посмотреть, какой должна быть кодировка. Я должен был угадать / сделать вывод. Надеюсь, это не будет иметь место для вас здесь.
std::string
это просто контейнер для 8-битной ширины char
и не знает / не заботится о кодировке. Всегда думайте в символах (буквы, цифры, знаки препинания и т. Д.). Первые 128 символов (0-127) были определены в соответствии со стандартом ASCII, поэтому для них требуется один char
хранить каждый символ. При наличии всех языков и символов мы не могли бы представить каждый из них с 256 возможностями. Кодировка UTF-8 представляет способ решения этой проблемы, позволяя одному символу принимать 1, 2, 3 или 4 char
широкий. Но для std::string
объект, это полностью прозрачно, и он все еще имеет дело с серией символов.
Причина, по которой вы думаете, что строка искажена, возможно потому, что ваш отладчик принимает содержимое std::string
всегда равен 1 символу на символ (например, расширенный ASCII), и поэтому отображает неправильные символы.
Изменить: вы также можете прочитать этот пост.