17 апреля 2006

Текстовые файлы в 21 веке

Стандартная библиотека С++ имеет хороший инструментарий для работы с текстовыми файлами. Однако, этот инструментарий немного устарел.

Вот пример кода, написанного с использованием стандартной библиотеки:
void process_file(const char *filename)
{
std::ifstream f(filename);
std::string s;
while (!f.eof())
{
std::getline(f, s);
process_line(s);
}
f.close();
}

На первый взгляд все замечательно, но только на первый. В реальности и в имени файла, и в самом тексте могут встретиться символы, не входящие в стандартный ASCII алфавит.

А проблема в приведенном фрагменте кода в двух местах:
1. const char *filename
2. std::string s;

С первым разобраться достаточно просто:
void process_file(const wchar_t *filename)
{
std::ifstream f(_wfopen(filename, L"rb"));
...
}

Казалось бы – и второе достаточно просто решается, заменой ifstream на wifstream. Однако, не все так просто как кажется. А загвоздка вот в чем – текстовые файлы в unicode могут быть в различных кодировках: UTF-8, UTF-16, ... Да и в принципе могут встретиться «старые добрые» файлы в ANSI кодировке.

Как и следовало полагать, «велосипед» по чтению unicode-файлов уже давно изобрели. Вообще, я ожидал его увидеть в boost-е, но его там не оказалось. Я нашел его... в contributes к boost-у. Причем эта библиотека датирована 2003 годом, но так и не попала в boost!

Библиотека отлично работает, сама разпознает кодировку по BOM. Самое интересное – в ней сразу нашлась небольшая ошибка:
facet_type* facet;
switch(is.get())
{
case ...:
case ...:
}
if(!facet)
{
...
}
return facet;

Присвоение переменной facet значения идет только в случаях, если выполняется один из case-ов, в любых других случаях она остается неинициализированной, о чем мне и сообщил VC 7.1. Вылечилось просто:
facet_type* facet = NULL;

Использование библиотеки очень простое:
std::wifstream input_file(file_name, std::ios_base::binary);
// autodetect the UTF encoding of this file
boost::utf::imbue_detect_from_bom(input_file);

// some reading
std::wstring s;
input_file >> s;

Только возникают небольшая (легко устранимая) проблема с интерпретацией конца строки в файле, открытом в режиме binary (CR LF уже не становится автоматически LF).

Вызывает сомнения то, что библиотека с 2003 года не обновлялась и так и не включилась в boost, да еще эта ошибка... Поиск в конференциях показал, что используют эту библиотеку очень редко. Значит используют что-то другое? Скорее всего тяжеловесную ICU.

Комментариев нет: