Не первый раз сталкиваюсь с задачей чтения UNICODE символов, строк из текстового файла стандартными средствами С++. И в очередной раз понимаю, как С++ устарел...
Первое решение, которое приходит в голову, - использовать классы std::wifstream и std::wstring. Но, к сожалению, результирующие строки заполнены мусором. Второй вариант - читать char-ы в режиме binary, после reinterpret_cast-ом получить wchar_t*. Но этот вариант очень неудобен, т.к. требует написания лишнего кода, нельзя пользоваться оператором >> и, как результат, istream_iterator.
Почему так? Во-первых, следует понять, что UNICODE - это не кодировка. Не совсем кодировка. UNICODE - это набор символов, а также способы их представления в 8-битной, 16-битной и 32-битной формах - кодировках UTF-8, UTF-16 и UTF-32 соответственно. Поэтому наш UNICODE
-файл может содержать символы, закодированные одним из перечисленных способов.
Текущая версия стандарта насчитывает более миллиона символов. UTF-16 способен представить лишь 65536 из них. Тип wchar_t имеет разную длинну в зависимости от платформы (ОС, компилятора, ...). Для Windows - 2 байта, для Linux - 4 и т.д. В итоге мы пытаемся читать файл с неизвестной кодировкой в неизвестное представление строки символов из набора UNICODE. В результате - мусор.
Свою проблему я решил следующим способом:
- Сохранил текст в кодировке UTF-16.
- Вежливо попросил
std::wifstream использовать специальный аспект (facet
) для перекодирования символов.
Стандартная библиотека содержит аспект std::codecvt. Компилятор, соответствующий стандарту, должен как минимум содержать специализацию std::codecvt<char, wchar_t, std::mbstate_t>, что позволяет читать ANSI символы в строку wchar_t. А нам нужно wchar_t в wchar_t. Для этого в библиотеке boost есть класс boost::archive::codecvt_null (#include <boost/archive/codecvt_null.hpp>).
Результирующий код:
std::wifstream &stream = ...;
std::locale loc(stream.getloc(), new boost::archive::codecvt_null<wchar_t>());
stream.imbue(loc);
std::vector<std:wstring> data(std::istream_iterator<std:wstring>(stream), std::istream_iterator<std:wstring>());
Кстати, есть еще utf8_codecvt_facet (#include <boost/detail/utf8_codecvt_facet.hpp>).