substr с символами вместо байтов
Предположим, у меня есть string s = "101870002PTäPO PVä #Person Tätigkeitsdarstellung 001100001&0111010101101870100092001000010"
Когда я делаю substring(30,40)
возвращает " #Person Tätigkeitsdarstellung", начиная с пробела. Я предполагаю, что он считает байты вместо символов.
Обычно размер строки составляет 110, и когда я делаю s.length()
или же s.size()
он возвращает 113 из-за 3 специальных символов.
Мне было интересно, если есть способ избежать этого пустого пространства в начале возвращаемого значения.
Спасибо за вашу помощь!
1 ответ
В utf-8 кодовая точка (символ) ä
состоит из двух блоков кода (которые составляют 1 байт в utf-8). C++ не поддерживает обработку строк как последовательности кодов. Следовательно, что касается стандартной библиотеки, std::string("ä").size()
это 2.
Простой подход заключается в использовании std::wstring
, wstring
использует тип символа (wchar_t
), который по крайней мере такой же широкий, как и самый широкий набор символов, поддерживаемый системой. Следовательно, если система поддерживает достаточно широкую кодировку для представления любого (не составного) символа юникода одной единицей кода, строковые методы будут вести себя так, как вы ожидаете. В настоящее время utf-32 достаточно широк и поддерживается (чаще всего?) Unix-подобной ОС.
Следует отметить, что Windows поддерживает только utf-16, а не utf-32, поэтому, если вы выберете wstring
Подойдите и перенесите вашу программу в Windows, и пользователь вашей программы попытается использовать символы Юникода, ширина которых превышает 2 байта, тогда предположение об одной единице кода на кодовую точку не выполняется.
wstring
подход также не принимает во внимание управляющие или составные символы.
Вот небольшой тестовый код, который преобразует std::string
содержащий многобайтовый символ utf-8 ä
и преобразует его в wstring
:
string foo("ä"); // read however you want
wstring_convert<codecvt_utf8<wchar_t>> converter;
wstring wfoo = converter.from_bytes(foo.data());
cout << foo.size() << endl; // 2 on my system
cout << wfoo.size() << endl; // 1 on my system
К сожалению, libstdC++ не реализован <codecvt>
который был введен в C++11 по крайней мере с gcc-4.8. Если вам не требуется libC++, то, вероятно, похожая функциональность есть в Boost.Locale.
В качестве альтернативы, если вы хотите сохранить свой код переносимым на системы, которые не поддерживают utf-32, вы можете продолжать использовать std::string
и использовать внешнюю библиотеку для итерации и подсчета и тому подобное. Вот один из них: http://utfcpp.sourceforge.net/ и другой: http://site.icu-project.org/. Я считаю, что это рекомендуемый подход.