Pull to refresh

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

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

Итак, первое с чем пришлось понять — что со строками в Линуксе нужно работать в кодировке 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 описывать не буду — все там достаточно хорошо документировано.
Tags:
Hubs:
+11
Comments 43
Comments Comments 43

Articles