Не первый раз сталкиваюсь с задачей чтения 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>).