Не так давно озадачился вопросом кроссплатформенной работы со строками в приложениях c++. Задача была, грубо говоря, поставлена как регистронезависимый поиск подстроки в любой кодировке на любой платформе.
Итак, первое с чем пришлось понять — что со строками в Линуксе нужно работать в кодировке UTF-8 и в типе std::string, а в Windows строки должны быть в UTF-16LE (тип std::wstring). Почему? Потому что это by design операционных систем. Хранить строки в std::wstring в Линуксе крайне накладно, поскольку один символ wchar_t занимает 4 байта (в Windows — 2 байта), а работать std::string в Windows нужно было во времена Windows 98. Для работы со строками определяем свой платформонезависимый тип:
Второе — задача преобразование текста из любой кодировки в тип mstring. Тут вариантов не так много. Первый вариант — использование std::locale и прочих соответствующих стандартных вещей. Сразу бросилось в глаза необходимость поиска для каждого charset'a соотвествующей ему локали (типа кодировке «windows-1251» соответствует локаль Russian_Russia.1251 и т.п.). В стандартной библиотеке такая таблица не нашлась (может плохо искал?), искать примочку для списка локалей не захотелось. Да и вообще, работа с локалалями в C++ вещь очень неочевидная, на мой взгляд. На форумах советовали использовать библиотеки libiconv или icu. libiconv выглядел очень легко и просто, с задачей перекодировки из любого charset'a в mstring справлялся отлично, но когда дело дошло до преобразования mstring в нижний регистр меня постиг fail. Оказалось libiconv делать это не умеет, а преобразовать строку utf8 в нижний регистр просто и красиво в Линуксе у меня не получилось. Итак, выбор пал на icu, который с честью решил все поставленные задачи (конвертация и перевод в нижний регистр). Процедура платформонезависимой перекодировки с использованием библиотеки icu выглядит примерно так:
Вопросы работы с Юникодом в Windows описывать не буду — все там достаточно хорошо документировано.
Итак, первое с чем пришлось понять — что со строками в Линуксе нужно работать в кодировке UTF-8 и в типе std::string, а в Windows строки должны быть в UTF-16LE (тип std::wstring). Почему? Потому что это by design операционных систем. Хранить строки в std::wstring в Линуксе крайне накладно, поскольку один символ wchar_t занимает 4 байта (в Windows — 2 байта), а работать std::string в Windows нужно было во времена Windows 98. Для работы со строками определяем свой платформонезависимый тип:
#ifdef _WIN32 typedef std::wstring mstring; #else typedef std::string mstring; #endif // _WIN32
Второе — задача преобразование текста из любой кодировки в тип mstring. Тут вариантов не так много. Первый вариант — использование std::locale и прочих соответствующих стандартных вещей. Сразу бросилось в глаза необходимость поиска для каждого charset'a соотвествующей ему локали (типа кодировке «windows-1251» соответствует локаль Russian_Russia.1251 и т.п.). В стандартной библиотеке такая таблица не нашлась (может плохо искал?), искать примочку для списка локалей не захотелось. Да и вообще, работа с локалалями в C++ вещь очень неочевидная, на мой взгляд. На форумах советовали использовать библиотеки libiconv или icu. libiconv выглядел очень легко и просто, с задачей перекодировки из любого charset'a в mstring справлялся отлично, но когда дело дошло до преобразования mstring в нижний регистр меня постиг fail. Оказалось libiconv делать это не умеет, а преобразовать строку utf8 в нижний регистр просто и красиво в Линуксе у меня не получилось. Итак, выбор пал на icu, который с честью решил все поставленные задачи (конвертация и перевод в нижний регистр). Процедура платформонезависимой перекодировки с использованием библиотеки icu выглядит примерно так:
std::string to_utf8(const std::string& source_str, const std::string& charset, bool lowercase) { const std::string::size_type srclen = source_str.size(); std::vector<UChar> target(srclen); UErrorCode status = U_ZERO_ERROR; UConverter *conv = ucnv_open(charset.c_str(), &status); if (!U_SUCCESS(status)) return std::string(); int32_t len = ucnv_toUChars(conv, target.data(), srclen, source_str.c_str(), srclen, &status); if (!U_SUCCESS(status)) return std::string(); ucnv_close(conv); UnicodeString ustr(target.data(), len); if (lowercase) ustr.toLower(); std::string retval; ustr.toUTF8String(retval); return retval; }
Вопросы работы с Юникодом в Windows описывать не буду — все там достаточно хорошо документировано.
