Итак, встала тривиальная задача: преобразовать строку из многобайтной в "широкую" и обратно.
Беглое изучение форумов привело к такому решению:
std::string in("Привет Мир!");
std::wstring(in.begin(), in.end());
Решение было найдено на одном из англоязычных форумов. И там оно действительно работало, так как
wchar_t test = L't';
char res = const_cast<char>(test);
установит res в 't' в локали UTF-8. Почему так? Потому что ASCII-часть в этой кодировке представлена 1 байтом. Проблемы могли бы возникнуть на системах с другим порядком байт. Но в основном - все бы работало. Для моего примера в результате получим строку. Но она будет состоять из "обкусаных" символов. Совершенно нечитаемые "кракозябры".
После некоторого изучения страшной и ужасной документации на std::locale я пришел к выводу, что наиболее правильный путь - использовать фасетку codecvt, которая как раз и предназначена для подобных случаев.
Итак, имеем следующий код (чисто вспомогательный):
typedef std::codecvt<wchar_t, char, mbstate_t> facet;
std::string ws2s(const std::wstring & in) const
{
std::string out(in.length(), '?'); // Начальное резервирование места
locale loc(""); // Создаем локаль
const facet & cvt = std::use_facet<facet>(loc); // Получаем ссылку на фасетку
mbstate_t state;
const wchar_t * fn;
const char * tn;
cvt.out(state, &in[0], &in[0] + in.length(), fn, &out[0], &out[0] + out.length(), tn); // Я не проверяю результат - это тестовый пример
return out;
}
Казалось бы, все нормально. Все должно работать. Ан нет!
Я вчера угробил часов 5 на то, чтобы понять, почему этот код выдает такое:
test: ../iconv/loop.c:425: utf8_internal_loop_single: Assertion `inptr - bytebuf > (state->__count & 7)' failed.
Aborted
Сегодня утром решил погуглить по ошибке. Проблема в том, что раньше я не работал с локалями и фасетками, и думал, что делаю что-то неправильно. Google нашел интересную дискуссию. Я ее с увлечением читал, пока не добрался до места:
Теперь все работает, ура!
std::string in("Привет Мир!");
std::wstring(in.begin(), in.end());
Решение было найдено на одном из англоязычных форумов. И там оно действительно работало, так как
wchar_t test = L't';
char res = const_cast<char>(test);
установит res в 't' в локали UTF-8. Почему так? Потому что ASCII-часть в этой кодировке представлена 1 байтом. Проблемы могли бы возникнуть на системах с другим порядком байт. Но в основном - все бы работало. Для моего примера в результате получим строку. Но она будет состоять из "обкусаных" символов. Совершенно нечитаемые "кракозябры".
После некоторого изучения страшной и ужасной документации на std::locale я пришел к выводу, что наиболее правильный путь - использовать фасетку codecvt, которая как раз и предназначена для подобных случаев.
Итак, имеем следующий код (чисто вспомогательный):
typedef std::codecvt<wchar_t, char, mbstate_t> facet;
std::string ws2s(const std::wstring & in) const
{
std::string out(in.length(), '?'); // Начальное резервирование места
locale loc(""); // Создаем локаль
const facet & cvt = std::use_facet<facet>(loc); // Получаем ссылку на фасетку
mbstate_t state;
const wchar_t * fn;
const char * tn;
cvt.out(state, &in[0], &in[0] + in.length(), fn, &out[0], &out[0] + out.length(), tn); // Я не проверяю результат - это тестовый пример
return out;
}
Казалось бы, все нормально. Все должно работать. Ан нет!
Я вчера угробил часов 5 на то, чтобы понять, почему этот код выдает такое:
test: ../iconv/loop.c:425: utf8_internal_loop_single: Assertion `inptr - bytebuf > (state->__count & 7)' failed.
Aborted
Сегодня утром решил погуглить по ошибке. Проблема в том, что раньше я не работал с локалями и фасетками, и думал, что делаю что-то неправильно. Google нашел интересную дискуссию. Я ее с увлечением читал, пока не добрался до места:
------- Comment #26 from rleigh at debian dot org 2006-06-18 09:51 -------Оказалось, достаточно было сделать явную инициализацию переменной состояния!
Thiemo Seufer diagnosed this as a problem with the testcases: mbstate_t needs
explictly initialising to all-bits-zero with memset. After doing this
std::memset(&state, 0, sizeof(mbstate_t));
all the testcases work for me on powerpc and i386.
Since this is not a bug, it can be closed. Sorry about that. Perhaps the
libstdc++ doxygen documentation for codecvt could document that
state_type/mbstate_t needs explicit initialisation before use.
Regards,
Roger
Теперь все работает, ура!