How to read unicode text file in C++
Не первый раз сталкиваюсь с задачей чтения 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>).
4 коммент.:
У меня нет проблем чтения Unicode файлов. ArchLinux, GCC 4.4.2. Вот пример чтения построчно файла.
// Start file "Unicode.cpp"
#include < iostream >
#include < fstream >
#include < string >
#include < vector >
using namespace std;
int main()
{
vector< string > v;
ifstream in("Unicode.cpp");
string line;
// Читаем файл построчно
while(getline(in, line))
v.push_back(line);
// Выводим из вектора по
// строке с добавлением номеров строк
for(int i=v.size(); i != 0; i--)
cout << i << ": " << v[i-1] << endl;
return(0);
}
Проблем не было только из-за того, что в файле был записан текст в UTF-8 и кодировка консоли - UTF-8.
Отправить комментарий