Файловый ввод/вывод с применением потоков Библиотека С++ содержит три специализированных класса для файлового ввода.вывода: ifstersm: Для операций с входным дисковым файлом ofsteram: Для операций с выходным дисковым файлом fstream: Для входных и выходных файлов Эти классы являются производными соответственно от istream, osteram и iosteram Т.о. они наследуют все их функциональные особенности (перегруженные операции <<, >> для встроенных типов, флаги формата, состояния потока и т.д.) Файловый ввод/вывод с применением потоков конструкторы файловых потоков открытие файла режимы доступа замена буфера потока закрытие буфера 1. Конструкторы файловых потоков Для каждого из трех классов файловых потоков прeдусмотрено четыре конструктора 1) Конструировать объект не открывая файла ifstersm(); ofsteram(); fstream(); 2) Конструировать объект, открыть файл и прикрепить объект к файлу ifsteram( const char *name, int omode = ios::in, int prot = filebuf::openprot ); ofsteram( const char *name, int omode = ios::out, int prot = filebuf::openprot ); fsteram( const char *name, int omode, int prot = filebuf::openprot ); 3) Конструировать объект и прикрепить объект существующему файлу. Указывается дескриптор файла к уже ifstersm(int f); ofsteram(int f); fstream(int f); 4) Конструировать объект, ассоциированный с указанным буфером, прикрепить объект к уже открытому файлу. Специфируется дескриптор файла ifstersm(int f, char *b, int len); ofsteram(int f, char *b, int len); fstream(int f, char *b, int len); 2. Открытие файла Чтобы открыть файл можно использовать конструкторы ifstersm, ofsteram, fstream Пример применения ifstersm, ofsteram #include <iostream.h> #include <fstream.h> #include <stdlib.h> #include <ctype.h> int main(void) { cout << "Введите имя входного файла: "; char fname[_MAX_PATH]; cin >> fname; ifstream ifs(fname); // Открыть поток if (!ifs) { cout << "Невозможно открыть файл"; return 0; } // Проверить поток cout << "Введите имя выходного файла:"; cin >> fname; ofstream ofs(fname); if (!ofs) { cout << "Невозможно открыть файл"; return 0; } char c; while (ifs && ofs) // Пока не произойдет ошибки { ifs.get(c); // Прочитать символ c =toupper(c); // Перевести в верхний режим ofs.put(c); // Записать в выходной поток cout << '.'; // Показать, что процесс жив } cout << endl << "Выходной файл является копией входного" << "в верхнем регистре" << endl; return 0; } Пример: открыть файл, применив метод open потока fstream #include <iostream.h> #include <fstream.h> #include <stdlib.h> int main(void) { fstream fin; char fname[_MAX_PATH]; cout << "Введите имя файла: "; cin >> fname; fin.open( fname, ios::in); // Открыть входной файл if (fin) // Проверить поток { cout << "Файл открыт успешно" << endl; fin.close(); // Закрыть файл } else { cout << "Ошибка" << endl; } return 0; } 3. Режимы доступа При открытии файла можно специфицировать параметр режим доступа, чтобы указать каким образом файл должен открываться Параметр можно составить из перечислителей битовых масок, определяемых классом ios. enum open_mode { in = 0x01, // Открыть для чтения out = 0x02, // Открыть для записи (подразумевает ios::trunc, // если не специфицированы ios::in, ios::ate, ios::app ate = 0x04, // искать eof при открытии app = 0x08, // Присоединить; добавить в конец файла // подразумевается ios::out) trunc = 0x10, // изменять файл, если уже существует nocreate =0x20, // показать ошибку, если файл не существует noreplace =0x40, // показать ошибку, если файл уже существует binary =0x80 // двоичный (не текстовый ) файл }; Параметр mode в конструкторах и методах классов ifstream и ofstream имеют значения по умолчанию. Это ios:in и ios::out по умолчанию. Пример использования параметра mode: #include <iostream.h> #include <fstream.h> const int MAX_LEN=80; const char fname[]= "NEWFILE"; int main(void) // Создать новый файл, если только он уже не существует { ofstream ofs (fname, ios:out | ios::noreplace); if (!ofs) { cout << "Ошибка" << fname << "уже существует" << endl; } else { ofs << "Привет, я новый файл"; ofs.close(); // Закрыть файл fstream fs; // Определить новый объект fs.open(fname, ios::out | ios::ate); // Открыть файл и установить его на EOF fs << "к которому сделано добавление"; fs.close; fstream ifs(fname); //Снова открыть как входной if (ifs) { cout << "Старый файл:" << endl; char line[MAX_LEN]; ifs.getline(line, sizeof(line)); cout << line; } else { cout << "Ошибка при повторном открытии" << fname << endl; } } return 0; } 4. Замена буфера потока Можно управлять буферизацией потока с помощью метода setbuf. Эта функция ассоциирует с потоком указанный буфер void setbuf(char *p, int len); p - адрес буфера len - длина буфера 5. Закрытие буфера В классах файловых потоков имеется метод close, который: опорожняет поток закрывает, закрепленный за потоком файл Если при попытке закрыть файл, происходит ошибка, устанавливается флаг failbit состояния потока. Предполагается, что деструктор файлового объекта (или его базового класса) автоматически закрывает класс. Неформатированный ввод/вывод чтение/запись необработанных данных чтение символа чтение строки Эти функции позволяют читать/записывать байты данных без модификации и часто применяются при работе с бинарными файлами. В бинарном режиме данные при вводе/выводе не интерпретируются. Байты читаются и записываются без какой-либо модификации. Чтобы открыть файл в бинарном режиме, включите флаг ios::binary в параметр open_mode, передаваемый конструктору потока или функции open. В следующем примере файл открывается в двоичном и текстовом форматах: #include <iostream.h> #include <fstream.h> #include <stdlib.h> int main(void) { fstream fin; char name[_MAX_PATH]; cout << "Введите имя входного файла: "; cin >> name; ifsream ifbin( name, ios::in | ios::binary); ifsream iftxt( name, ios::in); if (!iftxt || !ifbin) // Проверить поток { cout << "Ошибка при открытии файла " << name << endl; return 0; } int countInBin = 0; do { if (ifbin.get() != EOF) counInBin++; } while (ifbin); cout << "Число символов, прочитанных в режиме binary =" << countInBin << endl; int countInTxt = 0; do { if (iftxt.get() != EOF) counInTxt++; } while (iftxt); cout << "Число символов, прочитанных в режиме text =" << countInTxt << endl; return 0; } При открытии файла в текстовом режиме (без ios::binary): каждая пара возврат каретки/первод строки при вводе преобразуется в единственный символ перевода строки ( '\n' ) каждый перевод строки при выводе преобразуется в пару 1. Чтение/запись необработанных данных Функция read позволяет извлекать из потока указанное число символов, записывая их в буфер: istream& istream::read( char* p, int len); istream& istream::read( signed char* p, int len); istream& istream::read(unsigned char* p, int len); p - буфер для записи прочитанных символов len - максимальное число символов, которые должны быть прочитаны 1. Чтение/запись необработанных данных Функция write позволяет поместить в поток указанное число символов, из буфера: istream& istream::write( char* s, int n); istream& istream::write( signed char* s, int n); istream& istream::write(unsigned char* s, int n); s - буфер-источник n - число символов, которые должны быть записаны #include <iostream.h> #include <fstream.h> int i = 12345; long l=98765432; float f=4.536271; double d=2.4e12; char msg[]= "Hello"; const char bFname[]= "_IO.BIN"; const char tFname[]= "_IO.TXT"; int main(void) { // В текстовом режиме ofsream ofs( tFname); if (ofs) // Проверить поток { ofs << i <<'\t' << l <<'\t' << f <<'\t' << d <<'\t' << msg << endl; ofs.close(); } ifsream ifs( tFname); if (ifs) // Проверить поток { ifs >> i >> l >> f >> d >> msg; ifs.close(); } // Теперь в бинарном ofs.open(bName, ios::out | ios::binary); if (ofs) // Проверить поток { ofs.write( (char*)&i, sizeof(i) ); ofs.write( (char*)&l, sizeof(l) ); ofs.write( (char*)&f, sizeof(f) ); ofs.write( (char*)&d, sizeof(d) ); ofs.write( msg, sizeof(msg) ); ofs.close(); } ifs.open(bName, ios::out | ios::binary); if (ifs) // Проверить поток { ofs.read( (char*)&i, sizeof(i) ); ofs.read( (char*)&l, sizeof(l) ); ofs.read( (char*)&f, sizeof(f) ); ofs.read( (char*)&d, sizeof(d) ); ofs.read( msg, sizeof(msg) ); ifs.close(); } return 0; } 2. Чтение символа Для извлечения из потока одиночного символа можно использовать метод int istream::get( ) #nclude <iostream.h> int main(void) { int ch; cout << "Введите число за которым следует #:"; while ( (ch = cin.get() ) != '#' ) { if ( ch == EOF ) break; cout << (char)ch; } return 0; } 2. Чтение символа Можно применить также один из перегруженных вариантов get( ) istream& istream::get(char&); istream& istream::get(signed char&); istream& istream::get(unsigned char&); Например: #include <iostream.h> int main(void) { char ch; cout << "Введите число за которым следует #:"; while ( cin.get(ch) ) { if ( ch == '#' ) break; cout << (char)ch; } return 0; } 3. Чтение строки В библиотеке С++ имеются функции get и getline для чтения символов вплоть до ограничителя. Они часто используются для чтения строк get Метод istream& istream::get(char* _p, int _l, char _t) извлекает символы из потока до тех пор, пока не будет найден ограничитель _t (по умолчанию \n) будет прочитано _l символов встретится коней файла. Функция get не извлекает ограничитель из потока и не помещает его в буфер #include <iostream.h> #include <limits.h> const int MAX_LEN = 0x50; int main(void) { char fname[MAX_LEN]; char lname[MAX_LEN]; cout << "Введите ваше имя:"; cin.get(fname, sizeof(fname)); // Прочитать имя с помощью get cout << "Привет" << fname << endl; cout << "Введите вашу фамилию:"; cin.ignore(INT_MAX, '\n'); // Очистить поток //(ограничитель не был извлечен) cin.get(lname, sizeof(lname)); // Проитать имя с помощью get cout << "Привет" << lname << endl; return 0; } getline Этот метод тоже может быть использован для извлечения из потока символов до тех пор, пока не будет найден ограничитель _t (по умолчанию \n), не будет прочитано определенное число символов _l, или не встретится конец файла istream& istream::getline(char* _p, int _l, char _t) Функция getline извлекает ограничитель, но не записывает его в буфер. #include <iostream.h> const int MAX_LEN = 0x50; int main(void) { char fname[MAX_LEN]; char lname[MAX_LEN]; cout << "Введите ваше имя:"; cin.getline(fname, sizeof(fname)); // Проитать имя с помощью get cout << "Привет" << fname << endl; cout << "Введите вашу фамилию:"; cin.getline(lname, sizeof(lname)); // Проитать имя с помощью get cout << "Привет" << lname << endl; return 0; } Другие часто используемые функции Пропуск символов при вводе Проверка счетчика извлечений Заглядывание вперед Позиционирование потока Выяснение текущей позиции потока Пропуск символов при вводе Функция ignore istream& istream::ignore(int n=1, int d=EOF); Извлекает сиволы из потока (не более n символов, по умолчанию 1), пока не встретится ограничитель d (по умолчанию EOF) Проверка счетчика извлечений Функция gcount int istream::gcount( ); возвращает число символов, извлеченных последней функцией неформатированного ввода. Заглядывание вперед Функция peek; int istream::peek(); возвращает возвращает значение очередного символов, не извлекая его из входного потока функция возвращает EOF, если флаги состояния потока имеют ненулевое значение Позиционирование потока Функции seekg и seekp могут использоваться для позиционирования указателя извлечения/помещения соответственно входного и выходного потока: istream& istream::seekg(streampos); istream& istream::seekg(streamoff, ios::seek_dir); istream& istream::seekp(streampos); istream& istream::seekp(streamoff, ios::seek_dir); Выяснение текущей позиции потока Функции tellg и tellp позволяют найти текущую позицию соответственно входного и выходного потока strempos istream::tellg( ); strempos ostream::tellp( );