Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

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. В результате - мусор.

Свою проблему я решил следующим способом:

  1. Сохранил текст в кодировке UTF-16.
  2. Вежливо попросил 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 коммент.:

IT_Hamsteri комментирует...
Этот комментарий был удален автором.
IT_Hamsteri комментирует...

У меня нет проблем чтения 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.

Robert комментирует...
Этот комментарий был удален автором.

Отправить комментарий

Copyright 2007-2011 Chabster