Использовать широкие текстовые потоки для работы с текстовыми файлами не представляет труда. Напишем вариацию на тему примера из листингов 14.3 и 14.4 (листинг 14.26).
Листинг 14.26. Текстовые файлы с широкими текстовыми потоками #include <fstream>
#include <iostream> // требуется в Visual С++.NET 2003
#include <ctime>
using namespace std;
// функция копирования файлов
void filecopy (wifstream &in, wofstream &out)
{ wchar_t ch;
while(in.get(ch)) // читать все символы, в том числе пробельные
out.put(ch);
}
int main()
{ srand((unsigned)time(NULL)); // датчик случайных чисел
wofstream strm; // выходной поток-объект
strm.open("c:/textfiles/woonumber.txt"); // открываем
if (strm.is_open()) // проверка открытия
{ for(int i = 0; i < 10; i++)
strm << rand()%10 << endl; // выводим 10 чисел
strm.close(); // закрываем выходной поток-файл
// суммирование чисел записанных в файле
// открываем тот же текстовый файл для чтения
wifstream strm("с:/textfiles/woonumber.txt");
if (strm)
{ int number, summa = 0, count = 0; while(strm >> number) { ++count;
summa+=number;
// проверка открытия
// ввод числа
// подсчет количества
// суммирование
wcout << summa << L"; strm.closeO ;
<< count;
// вывод результатов // закрываем поток-файл
}
} s
wifstream instrm ("c:/textfiles/woonumber.txt");
wofstream outstrm("c:/textfiles/woonumber.new");
if (instrm) filecopy(instrm, outstrm); // копирование файлов
instrm.closeO; // закрываем файлы
outstrm.closeO ; return EXIT_SUCCESS;
}
Сначала программа объявляет широкий поток strm, который при открытии связывается с текстовым файлом woonumber.txt. Затем в файл выводятся 10 случайных чисел, и поток закрывается. На этой стадии в нашем каталоге TextFiles можно обнаружить текстовый файл размером 30 байт. Далее тот же файл открывается как входной, числа читаются и суммируются — все работает точно так же, как и в примере из листинга 14.3. Затем существующий файл копируется с помощью функции копирования f i lecopy (), параметрами которой являются широкие потоки. На этой стадии программы в каталоге TextFiles можно увидеть 2 файла: woonumber.txt и woonumber.new.
Широкие потоки в функции fi lecopy () использовать совершенно необязательно — вполне корректно работают и обычные, открытые в двоичном режиме:
ifstream instrm ("с:/textfiles/woonumber.txt", ios::binary); ofstrjeam outstrm("c:/textfiles/woonumber.new", ios::binary);
При этом не происходит преобразования формата символа новой строки '\п\ То есть считывание и запись можно выполнять побайтно — независимо от того, сколько и каких байтов в файл записано.
Размеры файлов показывают, что символы-цифры выводятся в «широкий» файл в однобайтовом виде. Это, кстати, явно указано в стандарте (см. п. п. 28.8.1/2 в [1]). Проверим, как выводятся широкие символы латиницы и кириллицы. Для этого создадим два широких выходных файловых потока. Для «чистоты» эксперимента установим для одного из них русский контекст и выведем в поток русскоязычную и англоязычную константу. Для второго потока контекст устанавливать не будем. Добавим в конец предыдущего примера следующие строки:
wofstream rstrm("c:/textfiles/woorussian.txt"); rstrm.imbue(locale("rus_rus.866"));
rstrm << 1_"Проверка вывода русских букв в файл" << endl; rstrm << L"aaaaaaaa www rrrrrrr cccc i ffff" << endl; rstrm.closeO ;
Программа работает совершенно одинаково и в Visual C++.NET 2003, и в С++ Builder 6.
wofstream estrm("c:/textfiles/wooenglish.txt");
estrm << L"aaaaaaaa vvvvvv rrrrrrr cccc i ffff" << endl;
estrm << 1_"Проверка вывода русских букв в файп" << endl; // не выводятся
estrm.close();
И в «русский»^ и в «английский» потоки символы выводятся в однобайтной кодировке. Если русский контекст не установлен, то буквы кириллицы просто не выводятся в файл — как и все остальные символы, расположенные следом!
Ничего не меняется, если мы используем вместо широкой константы-строки широкую переменную:
wstring ws = !_"Проверка вывода русских букв в файп"; rstrm << ws << endl;
Размер русскоязычной строки в файле остается тем же самым.
ПРИМЕЧАНИЕ
Если широкие потоки, связанные с текстовыми файлами, работают правильно, то при связывании их с двоичными файлами возникают малопонятные проблемы. Во всяком случае, и в Visual C++.NET 2003, и в С++ Builder 6 методы read() и write() для широких потоков работают неправильно.
Резюме
Ввод-вывод в С++ основан на концепции потоков, которые были реализованы еще в С в библиотеке <stdio.h>. Библиотека <iostream> предоставляет объектно-ориентированную реализацию потоков. Объектно-ориентированный подход надежнее и проще в использовании.
В С++ реализован достаточно богатый набор средств ввода-вывода: стандартные консольные потоки, строковые потоки, файловые потоки. Объектно-ориентированные потоки более универсальны, чем процедурные, и «умеют» работать со строками типа string.
Для ввода-вывода информации в библиотеке перегружены операции сдвига влево и вправо. Кроме того, реализовано множество методов обмена данными между программой и потоком. В объектно-ориентированной библиотеке реализовано значительное количество средств форматирования: флаги, манипуляторы и методы. Все средства форматирования работают единообразно для всех видов потоков: стандартных, строковых и файловых.
Объектно-ориентированные средства ввода-вывода являются расширяемыми, то есть мы можем создавать собственные средства форматирования на основе стандартных. Перегрузка операций ввода и вывода обеспечивает их единообразный вид как для встроенных типов, так и для реализованных классов.
Строковые потоки являются универсальным средством преобразования чисел в строки и обратно. Строковые потоки бывают входными, выходными и двунаправленными. Для строковых потоков работают методы, обеспечивающие произвольный доступ к любому байту потока. |