Кроссплатформенная работа со строками на C++

в 13:01, , рубрики: c++, linux, windows, Песочница, метки: , ,

Не так давно озадачился вопросом кроссплатформенной работы со строками в приложениях c++. Задача была, грубо говоря, поставлена как регистронезависимый поиск подстроки в любой кодировке на любой платформе.

Итак, первое с чем пришлось понять — что со строками в Линуксе нужно работать в кодировке UTF-8 и в типе std::string, а в Windows строки должны быть в Юникоде (тип 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)
{
std::string retval;
UErrorCode status = U_ZERO_ERROR;
UConverter *conv = ucnv_open(charset.c_str(), &status);

std::string::size_type srclen = source_str.size();
UChar *target = new UChar[srclen];

if (target != NULL)
{
int32_t len = ucnv_toUChars(conv, target, srclen, source_str.c_str(), srclen, &status);
UnicodeString ustr(target, len);
delete[] target;

if (lowercase)
ustr.toLower();

ustr.toUTF8String(retval);
}

ucnv_close(conv);

return retval;
}

Вопросы работы с Юникодом в Windows описывать не буду — все там достаточно хорошо документировано.

Автор: getaclue


  1. Дмитрий:

    Хранить строки в std::wstring в Линуксе крайне накладно. А разве это не создает дополнительного удобства в виде одинакового размера символов?

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js