Организация файла последовательного доступа
В С++ файлу не предписывается никакая структура. Для последовательного поиска данных в файле программа обычно начинает считывать данные с начала файла до тех пор, пока не будут считаны требуемые данные. При поиске новых данных этот процесс вновь повторяется. Данные, содержащиеся в файле последовательного доступа, не могут быть модифицированы без риска разрушения других данных в этом файле. Например, если в файле содержится информация: Коля 12 Александр 52 то при модификации имени Коля на имя Николай может получиться следующее: НиколайАлександр 52 Аналогично в последовательности целых чисел 12 –1 132 32554 7 для хранения каждого из них отводится sizeof(int) байт. А при форматированном выводе их в файл они занимают различное число байт. Следовательно, такая модель ввода-вывода неприменима для модификации информации на месте. Эта проблема может быть решена перезаписью (с одновременной модификацией) в новый файл информации из старого. Это сопряжено с проблемой обработки всей информации при модификации только одной записи. Следующая программа выполняет перезапись информации из одного файла в два других, при этом в первый файл из исходного переписываются только числа, а во второй - вся остальная информация. #include "fstream.h" #include "stdlib.h" #include "math.h" void error(char *s1,char *s2="") // вывод сообщения об ошибке { cerr<<s1<<" "<<s2<<endl; // при открытии потока для файла exit(1); } main(int argc,char **argv) { char *buf=new char[20]; int i; ifstream f1; // входной поток ofstream f2,f3; // выходные потоки f1.open(argv[1],ios::in); // открытие исходного файла if(!f1) // проверка состояния потока error("ошибка открытия файла",argv[1]); f2.open(argv[2],ios::out); // открытие 1 выходного файла
if(!f2) error("ошибка открытия файла",argv[2]); f3.open(argv[3],ios::out); // открытие 2 выходного файла if(!f3) error("ошибка открытия файла",argv[3]); f1.seekg(0); // установить текущий указатель в начало потока while(f1.getline(buf,20,' ')) // считывание в буфер до 20 символов { if(int n=f1.gcount()) // число реально считанных символов buf[n-1]='\0'; // проверка на только цифровую строку for(i=0;*(buf+i)&&(*(buf+i)>='0' && *(buf+i)<='9');i++); if(!*(buf+i)) f2 <<::atoi(buf)<<' '; // преобразование в число и запись // в файл f2 else f3<<buf<<' '; // просто выгрузка буфера в файл f3 } delete [] buf; f1.close(); // закрытие файлов f2.close(); f3.close(); } В программе для ввода имен файлов использована командная строка, первый параметр – имя файла источника (входного), а два других – имена файлов приемников (выходных). Для работы с файлами использованы функции – open, close, seekg, getline и gcount. Более подробное описание функций приведено ниже. Ниже приведена программа, выполняющая ввод символов в файл с их одновременным упорядочиванием (при вводе) по алфавиту. Использование функций seekg, tellg позволяет позиционировать текущую позицию в файле, то есть осуществлять прямой доступ в файл. Обмен информацией с файлом осуществляется посредством функций get и put. #include "fstream.h" #include "stdlib.h" void error(char *s1,char *s2="") { cerr<<s1<<" "<<s2<<endl; exit(1); } main() { char c,cc; int n; fstream f; // выходной поток streampos p,pp; f.open("aaaa",ios::in|ios::out); // открытие выходного файла if(!f) error("ошибка открытия файла","aaaa"); f.seekp(0); // установить текущий указатель в начало потока while(1) { cin>>c; if (c=='q' || f.bad()) break; f.seekg(0,ios::beg); // перемещение указателя в начало файла while(1) { if(((cc=f.get())>=c) || (f.eof())) { if(f.eof()) // в файле нет символа большего с { f.clear(0); p=f.tellg(); // позиция ЕOF в файле } else { p=f.tellg()-1; // позиция первого символа большего с f.seekg(-1,ios::end); // указатель на последний элемент в файле pp=f.tellg(); // позиция последнего элемента в файле while(p<=pp) // сдвиг в файле на один символ
{ cc=f.get(); f.put(cc); if (--pp>0) f.seekg(pp); // проверка на невыход за начало файла } } f.seekp(p); // позиционирование указателя в позицию p f.put(c); // запись символа с в файл break; } } } f.close(); return 1; } Каждому объекту класса istream соответствует указатель get (указывающий на очередной вводимый из потока байт) и указатель put (соответственно на позицию для вывода байта в поток). Классы istream и ostream содержат по 2 перегруженные компоненты-функции для перемещения указателя в требуемую позицию в потоке (связанном с ним файле). Такими функциями являются seekg (переместить указатель для извлечения из потока) и seekp (переместить указатель для помещения в поток). istream& seekg(streampos pos); istream& seekg(streamoff off, ios::seek_dir dir); Сказанное выше справедливо и для функций seekp. Первая функция перемещает указатель в позицию рos относительно начала потока. Вторая перемещает соответствующий указатель на off (целое число) байт в трех возможных направлениях: ios::beg (от начала потока), ios::cur (от текущей позиции) и ios::end (от конца потока). Кроме рассмотренных функций в этих классах имеются еще функции tellg и tellp, возвращающие текущее положение указателей get и put соответственно streampos tellg(); Создание файла произвольного доступа Организация хранения информации в файле прямого доступа предполагает доступ к ней не последовательно от начала файла по некоторому ключу, а непосредственно, например, по их порядковому номеру. Для этого требуется, чтобы все записи в файле были бы одинаковой длины. Наиболее удобными для организации произвольного доступа при вводе-выводе информации являются компоненты-функции istream::read и ostream::write. При этом, так как функция write (read) ожидает первый аргумент типа const сhar* (char*), то для требуемого приведения типов используется оператор явного преобразования типов: istream& istream::read(reinterpret_cast<char *>(&s), streamsize n); ostream& ostream::write(reinterpret_cast<const char *>(&s), streamsize n); Ниже приведен текст программы организации работы с файлом произвольного доступа на примере удаления и добавления в файл информации о банковских реквизитах клиента (структура inf). #include<iostream> #include<fstream> #include<string> using namespace std; Struct inf { char cl[10]; // клиент int pk; // пин-код double sm; // сумма на счете } cldata; Class File { char filename[80]; // имя файла
fstream *fstr; // указатель на поток int maxpos; // число записей в файле public: File(char *filename); ~File(); int Open(); // открытие нового файла const char* GetName(); int Read(inf &); // счивание записи из файла void Remote(); // переход в начало файла void Add(inf); // добавление записи в файл int Del(int pos); // удаление записи из файла }; ostream& operator << (ostream &out, File &obj) { inf p; out << "File " << obj.GetName() << endl; obj.Remote(); while(obj.Read(p)) out<<"\nКлиент -> "<<p.cl<<' '<<p.pk<<' '<<p.sm; return out; } File::File(char *_filename) // конструктор { strncpy(filename,_filename,80); fstr = new fstream(); } File::~File() // деструктор { fstr->close(); delete fstr; } int File::Open() // функция открывает новый файл для ввода-вывода { fstr->open(filename, ios::in | ios::out | ios::binary| ios::trunc); // бинарный if (!fstr->is_open()) return -1; return 0; } int File::Read(inf &p) // функция чтения из потока fstr в объект p { if(!fstr->eof() && // если не конец файла fstr->read(reinterpret_cast<char*>(&p),sizeof(inf))) return 1; // fstr->clear(); return 0; } void File::Remote() // сдвиг указателей get и put в начало потока { fstr->seekg(0,ios_base::beg); // сдвиг указателя на get на начало fstr->seekp(0,ios_base::beg); // сдвиг указателя на put на начало fstr->clear(); // чистка состояния потока } const char* File::GetName() // функция возвращает имя файла { return this->filename; } void File::Add(inf cldata) { fstr->seekp(0,ios_base::end); fstr->write(reinterpret_cast<char*>(&cldata),sizeof(inf)); fstr->flush(); } int File::Del(int pos) // функция удаления из потока записи с номером pos { Remote(); fstr->seekp(0,ios_base::end); // для вычисления maxpos maxpos = fstr->tellp(); // позиция указателя put maxpos/=sizeof(inf); if(maxpos<pos) return -1;
fstr->seekg(pos*sizeof(inf),ios::beg); // сдвиг на позицию, // следующую за pos while(pos<maxpos) { fstr->read(reinterpret_cast<char*>(&cldata),sizeof(inf)); fstr->seekp(-2*sizeof(inf), ios_base::cur); fstr->write(reinterpret_cast<char*>(&cldata),sizeof(inf)); fstr->seekg(sizeof(inf),ios_base::cur); pos++; } strcpy(cldata.cl,""); // для занесения пустой записи в cldata.pk=0; // конец файла cldata.sm=0; fstr->seekp(-sizeof(inf), ios_base::end); fstr->write(reinterpret_cast<char*>(&cldata),sizeof(inf)); fstr->flush(); // выгрузка выходного потока в файл } int main(void) { int n; File myfile("file"); if(myfile.Open() == -1) { cout << "Can't open the file\n"; return -1; } cin>>cldata.cl>>cldata.pk>>cldata.sm; myfile.Add(cldata); cout << myfile << endl; // просмотр файла cout << "Введите номер клиента для удаления "; cin>>n; if(myfile.Del(n) == -1) cout << "Клиент с номером "<<n<<" вне файла\n"; cout << myfile << endl; // просмотр файла }
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|