codecvt

Feb. 24th, 2008 01:56 pm
madf: (Default)
[personal profile] madf
Итак, встала тривиальная задача: преобразовать строку из многобайтной в "широкую" и обратно.
Беглое изучение форумов привело к такому решению:

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
Оказалось, достаточно было сделать явную инициализацию переменной состояния!
Теперь все работает, ура!

Profile

madf: (Default)
madf

April 2018

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 7th, 2026 12:15 am
Powered by Dreamwidth Studios