Как записать структуру в файл в си
Есть у меня некая структура (struct).
Требуется создать файл, куда писать данные в виде этих структур. А также читать. А также перемещаться по файлу.
Как это сделать? (Плиз с ма-аленьким примером или в какую сторону копать).
Заранее спасибо.
Запись и чтение структур в/из файл(а)
От: | IT | linq2db.com |
Дата: | 11.10.02 04:19 | |
Оценка: | 100 (18) +2 -2 | |
#Имя: | FAQ.cpp.struct2file |
Почему ламерский? Нормальный вопрос, который обычно встречается большинству программеров.
ПА>Есть у меня некая структура (struct).
ПА>Требуется создать файл, куда писать данные в виде этих структур. А также читать. А также перемещаться по файлу.
ПА>Как это сделать? (Плиз с ма-аленьким примером или в какую сторону копать).
Всё зависит от многих вещей, как всегда
Для начала давай объявим твою структуру:
struct mystruct < int i; char buf[20]; double d; >;
Теперь, допусти, нам нужно положить её в файл средствами C/C++.
Семейсво функций FILE рассматривать не будет в связи с её архаичносью, и начнём сразу с варианта, который не входит в стандарт, но присутствует во многих компиляторах.
#include #include #include #include #include struct mystruct < int i; char buf[5]; double d; >; int main(int argc, char* argv[]) < // открываем файл int fh = _open("file.dat",_O_RDWR | _O_BINARY); if (fh == -1) < // или при необходимости создаём новый fh = _creat("file.dat",_S_IREAD | _S_IWRITE); if (fh == -1) // не шмагла :xz: return 1; > // готовим структуру для записи mystruct ms; memset(&ms,0,sizeof ms); ms.i = 1; ms.d = 2; // позиционируемся в конец файла _lseek(fh,0,SEEK_END); // добавляем новую структуру _write(fh,&ms,sizeof ms); // позиционируемся в начало _lseek(fh,0,SEEK_END); // читаем первую записанную структуру _read(fh,&ms,sizeof ms); return 0; >
Эта программа открывает файл (либо создаёт его при необходимости) и добавляет в него новую структуру, затем читает первый экземпляр.
Всё казалось бы нормально, но если ты посмотришь размер созддаваемого файла, то он всегда будет кратен 24 байтам (вариант Visual C++), хотя размер структуры равен 4+5+8=17 байт. Это происходит потому, что компиляторы по умолчанию выравнивают размер структур в целях оптимизации. Следовательно, наша первая задача отменить это поведение по умолчанию. Стандартных средств сделать это нет, но как правило компиляторы содержат специальную опцию коммандной строки и/или прагму, позволяющую это делать.
Ещё одной неверной деталью в нашем примере является использование типа переменной int. Для разных версий операционных систем размер инта может быть разным и лучше явно указать размер используемого типа — short или long.
Изменим описание структуры:
#pragma pack(push,1) struct mystruct < long i; char buf[5]; double d; >; #pragma pack(pop)
Теперь запись в файл даст вполне ожидаемый результат.
Здесь можно отметить ещё одну деталь. В качестве строки я использовал массив char[5]. Использование классов типа CString std::string не приведёт ни к чему хорошему. Фактически ты сохранишь не саму строку, а содержимое класса, который её реализует. Допустим, класс CMyString реализован следующим образом:
class CMyString < public: int len; char *str; // . >;
Объявление такой структуры как
struct mystruct < long i; CMyString str; double d; >;
будет фактически соответствовать следующему варианту:
struct mystruct < long i; int str_len; char *str_str; double d; >;
Т.е. в месте, где ты ожидаешь строку будет указатель на буфер в памяти, который (в смысле не буфер, а указатель на него) ты благополучно и сохранишь в файле.
Теперь рассмотрим вариант с потоками. Вообще-то, лучше конечно использовать новую версию , но у меня она до сих не вызывает никакого доверия. По-этому, воспользуемся старым вариантом:
#include #include #pragma pack(push,1) struct mystruct < long i; char buf[5]; double d; >; #pragma pack(pop) int main(int argc, char* argv[]) < // создаём или открываем файл fstream f("file.dat",ios::binary|ios::in|ios::out); // готовим структуру для записи mystruct ms; memset(&ms,0,sizeof ms); ms.i = 1; ms.d = 2; // позиционируемся в конец файла f.seekp(0,ios::end); // добавляем новую структуру f.write((unsigned char*)&ms,sizeof ms); // позиционируемся в начало f.seekp(0,ios::beg); // читаем первую записанную структуру f.read((unsigned char*)&ms,sizeof ms); return 0; >
Ниже вариант использования Windows API вместо фуекций CRTL:
#include #pragma pack(push,1) struct mystruct < long i; char buf[5]; double d; >; #pragma pack(pop) int main(int argc, char* argv[]) < // создаём или открываем файл HANDLE fh = ::CreateFile( TEXT("file.dat"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // готовим структуру для записи mystruct ms; memset(&ms,0,sizeof ms); ms.i = 1; ms.d = 2; // позиционируемся в конец файла ::SetFilePointer(fh,0,0,FILE_END); // добавляем новую структуру DWORD dw=0; ::WriteFile(fh,&ms,sizeof ms,&dw,NULL); // позиционируемся в начало ::SetFilePointer(fh,0,0,FILE_BEGIN); // читаем первую записанную структуру ::ReadFile(fh,&ms,sizeof ms,&dw,NULL); return 0; >
Еще можно привести вариант исользования класса CFile из MFC, но, в принципе, он будет не очень сильно отличаться.
Может я немного увлёкся, но главное чтобы было понятно
ЗЫ. Далше тебя ждут другие вопросы:
- Как узнать количество записанных структу в файле?
Правильный ответ — не вычислять это по размеру файла, а добавить в начало заголовок (специальную структуру), содержащую необходимую служебную информацию: фактический размер файла, версию формата, число записей, смещение к первому блоку и т.п. - Как добавлять записи переменной длины?
Можно к каждой записи добавить свой заголовок, описывающий её структуру. - Как удалять ненужные записи из файла?
Можно просто помечать их как удалённые, а в последствии организовать упаковку файла. Можно организовать список удалённых страниц и использовать их в дальнейшем вместо добавления новых в конец. - Как обеспечить совместный доступ к файлу из нескольких программ.
Блокировки, отдельный сервер доступа и ещё куча всяких вариантов. - Как сделать динамическую структуру записей в файле.
У-у-у.
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Про файлы.
От: | Панкратов Александр |
Дата: | 11.10.02 04:43 |
Оценка: |
Огромное спасибо, очень исчерпывающе.
IT>После всего этого возникает вполне законный вопрос — а может лучше сразу взять стандартную базу данных?
IT>Шутю я, шутю.
Если бы речь шла об обычных виндах — я бы так и сделал
Однако все происходит в WinCE. Да еще на обкоцанном варианте Casio. Поэтому с ADOCE возникают некоторые проблемы.
Re[2]: Переносимость short и long ?
От: | Vi2 | http://www.adem.ru |
Дата: | 11.10.02 04:52 | |
Оценка: |
Здравствуйте IT, Вы писали:
IT>Ещё одной неверной деталью в нашем примере является использование типа переменной int. Для разных версий операционных систем размер инта может быть разным и лучше явно указать размер используемого типа — short или long.
Интересно, неужели для разных версий операционных систем размеры short или long, в отличие от int, одинаковы?
Насколько я помню, есть sizeof(short int)
Так что и использование short или long не даст переносимости.
Vita Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[3]: Про файлы.
От: | orangy | http://twitter.com/orangy |
Дата: | 11.10.02 06:19 | |
Оценка: | 6 (1) |
Здравствуйте Панкратов Александр, Вы писали:
IT>>После всего этого возникает вполне законный вопрос — а может лучше сразу взять стандартную базу данных?
ПА>Однако все происходит в WinCE. Да еще на обкоцанном варианте Casio. Поэтому с ADOCE возникают некоторые проблемы.
Тогда у тебя возникает еще ряд проблем:
— CE система юникодная, осторожнее со строками при записи и чтении
— не помню какой Endian на Casio (это кажется был MIPS), осторожнее с переносом файлов с win32
— нельзя игнорировать alignment, точнее он должен быть на 4 байта. MIPS не позволяет адресовать 32-битное число по невыровненному адресу
это важно, если ты собираешься читать массивами структур
I would recommend using WINAPI CreateFile, ReadFile, . functions тьфу блин, совсем зарапортавался.
Используй WINAPI CreateFile, ReadFile, . — в winCE другого может и не быть, лучше всего завернуть это дело в портабельную оболочку.
Всегда указывай полный путь до файла, в WinCE нет понятия «текущая директория», если нужно — возьми у текущего модуля.
TCHAR buf[_MAX_PATH+30];
GetModuleFileName(hInstance, buf, _MAX_PATH);
Если объёмы данных небольшие — используй XML. Была где-то библиотека портированная для CE, поищи.
Янус 1.0 alpha 10: Orangy
«Develop with pleasure!»
Re[3]: Переносимость short и long ?
От: | Павел Кузнецов |
Дата: | 11.10.02 08:13 |
Оценка: |
Здравствуйте Vi2, Вы писали:
IT>>Ещё одной неверной деталью в нашем примере является использование типа переменной int. Для разных версий операционных систем размер инта может быть разным и лучше явно указать размер используемого типа — short или long.
Здесь IT был не вполне точен.
Vi2>Интересно, неужели для разных версий операционных систем размеры short или long, в отличие от int, одинаковы?
Не только для разных операционных систем, но и для разных компиляторов на одной операционной системе размеры short, long и int могут различаться.
Vi2>Насколько я помню, есть sizeof(short int)
Кроме указанного соотношения есть еще требования к минимальному диапазону представляемых значений:
short -32767 . . . +32767 (минимум 16 бит) int -32767 . . . +32767 (минимум 16 бит) long -2147483647 . . . +2147483647 (минимум 32 бита)
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: Переносимость short и long ?
От: | IT | linq2db.com |
Дата: | 11.10.02 11:31 | |
Оценка: |
Здравствуйте Vi2, Вы писали:
Vi2>Интересно, неужели для разных версий операционных систем размеры short или long, в отличие от int, одинаковы?
Ну если считать Windows 3.1 и Windows 95 разными версиями то да К тому же MS выпустила там чего-то 64 разрядное кажется, там уже и long поплывёт.
Vi2>Насколько я помню, есть sizeof(short int)
Vi2>Так что и использование short или long не даст переносимости.
Ладно придираться, я только обозначил проблему.
Если нам не помогут, то мы тоже никого не пощадим.
Re: Запись и чтение структур в/из файл(а)
От: | AndrewS42 |
Дата: | 06.06.05 06:58 |
Оценка: |
Здравствуйте, IT, Вы писали:
IT>Всё зависит от многих вещей, как всегда
Это точно! Несмотря на то, что топику уже несколько лет, я хотел бы вставить свои 5 копеек в обсуждаемую тему, тем более, что эта тема находится в статьях сайта, а значит не имеет срока давности.
Наиболее правильным и переносимым (но медленным) будет вообще не сохранять в файл структуру целиком. Более того, не желательно даже int записывать в файл напрямую. Одна из причин уже была озвучена в топике: несовпадающие размеры int в разных компиляторах даже на одной платформе. Другая причина кроется в переносимости. Не факт, что ваша программа (или даже её отдельный модуль) никогда не переедут под UNIX. В отличие от Windows, UNIX существует на большом зоопарке платформ. Некоторые из них имеют big-endian представления чисел (первым идёт старший байт). Если на такой платформе сохранить int в файл как есть, а затем прочитать этот файл на Intel-платформе, то мы получим совершенно другое число. Например (пусть размер int будет 32 бита), если на big-endian сохранили 0x12345678, то в little-endian прочитаем 0x78563412.
Для большинства эти детали не существенны, так как не так уж и много программистов (надо признаться, и я в том числе) пишут сразу для нескольких платформ. Но, по крайней мере, задуматься о потенциальных проблемах переносимости надо.
Как же сохранять данные в файл, чтобы и файл, и программа его читающая были переносимы? Ответ уже был в этом топике: сохранять всё в xml или другом текстовом файле. Однако, если объём данных велик, и от двоичных данных никуда не деться, то сохранять все данные следует побайтно. Пример сохранения 32-битного int (предполагаем, что система, где char имеет размер, не равный 8 бит, нам не попадётся):
#include const char filename[] = "file.dat"; void read_int(FILE* f, int* d) < unsigned char buf[4]; unsigned char* p = buf; fread(buf, sizeof(buf), 1, f); // portable *d = *p++; *d += *p++ // fread(d, sizeof(*d), 1, f); // not portable > // read_int void write_int(FILE* f, int d) < unsigned char buf[4]; unsigned char* p = buf; *p++ = d & 0xff; *p++ = (d >> 8) & 0xff; *p++ = (d >> 16) & 0xff; *p = (d >> 24) & 0xff; fwrite(buf, sizeof(buf), 1, f); // portable // fwrite(&d, sizeof(d), 1, f); // not portable > // write_in void help() < fprintf(stderr, "Usage: fileio \n" ); exit(1); > // help() int main(int argc, char *argv[]) < const int data = 0x12345678; FILE* fi; FILE* fo; int tmp; char err_msg[255]; if (argc != 2) help(); if (! strcmp(argv[1], "w")) < fo = fopen(filename, "w"); if (! fo) < sprintf(err_msg, "Can not open file '%s'", filename); perror(err_msg); exit(1); > write_int(fo, data); fclose(fo); > else if (! strcmp(argv[1], "r")) < fo = fopen(filename, "r"); if (! fo) < sprintf(err_msg, "Can not open file '%s'", filename); perror(err_msg); exit(1); > read_int(fo, &tmp); fclose(fo); printf("Readed %#08x. Must be %#08x\n", tmp, data); > else help(); return 0; > // main()
Если гложут вопросы быстродействия, особенно в свете того, что big-endian систем не много и ради этой редкости мы замедляем считывание файлов, то необходимо ввести условную компиляцию: на little-endian системе читать int целиком, на big-endian – побайтно. Опять же, нельзя забывать про отличия размера int на разных платформах.
Хочу отметить, что я далеко не гуру в этом вопросе, и все мои замечания основаны лишь на изучении и портировании Open source ПО на компьютер RM200 (RISC-процессор R4000 (big-endian), ОС SINIX).
Всего Вам
Андрей
Re[2]: Запись и чтение структур в/из файл(а)
От: | MaximE |
Дата: | 06.06.05 07:20 |
Оценка: |
[]
> Как же сохранять данные в файл, чтобы и файл, и программа его читающая были переносимы? Ответ уже был в этом топике: сохранять всё в xml или другом текстовом файле. Однако, если объём данных велик, и от двоичных данных никуда не деться, то сохранять все данные следует побайтно. Пример сохранения 32-битного int (предполагаем, что система, где char имеет размер, не равный 8 бит, нам не попадётся):
Автор: MaximE
Дата: 02.04.05
Автор: MaximE
Дата: 06.04.05
—
Maxim Yegorushkin
Как записать структуру в файл на языке C++?
Нужно создать приложение которое бы записывала структуру в файл и выводило из файла на экран.
Попытался это реализовать.
Но к сожалению не записывает всю структура в файл. Или некорректно записывает.
Вот сам код который я пытался реализовать:
#include "stdafx.h" #include "string.h" #include "stdio.h" #include #include using namespace std; void inputf(ifstream &f, char a[300], char b[300], char c[300], char d[300]); void outputf1(ofstream &f, char a[300], char b[300], char c[300], char s[40]); void main() // Основная функция < int c; setlocale(LC_ALL, "Russian"); cout > c; if (c == 2) < int const N = 3; int i,j=0; struct vvod < char fam[300]; char id[300]; char number[300]; >; vvod v[N]; for (i = 0; i < N; i++) < cout > v[i].fam; cout > v[i].id; cout > v[i].number; > ofstream f1; for (i = 0; i < N; i++) < outputf1(f1, v[i].fam, v[i].id, v[i].number, "writer.txt"); >> else if (c == 1) < char str[40];//Строковая переменная (для пути к файлу) cout > str; char v[900]; char q[300]; char w[300]; char e[300]; ifstream f; inputf(f,str,q,w,e); > > void outputf1(ofstream &f, char a[300], char b[300], char c[300], char s[40])//Функция для записи < f.open(s);//Открываем файл //Проверка успешности открытия файла: if (f.fail()) < cout f void inputf(ifstream &f, char a[300],char b[300], char c[300],char d[300] )//Функция для чтения < f.open(a);//Открываем файл //Проверка успешности открытия файла: if (f.fail()) < cout f >> b;//Читаем переменную из файла f >> c; f >> d; cout
Подскажите где накосячил.
- Вопрос задан более трёх лет назад
- 3285 просмотров
C++. Примеры использования средств C++ для работы с файлами
Примеры использования средств C++ для работы с файлами
В теме приводятся примеры использования файловой системы C++ для:
- чтения информации из файлов;
- записи информации в файлы.
Поиск на других ресурсах:
1. Функция, читающая строки из клавиатуры и записывающая их в файл
#include #include using namespace std; // Функция читает строки из клавиатуры и записывает их в файл void Example1(const char* filename) < ofstream os(filename); // текстовый файл для вывода // проверка, открыт ли файл if (!os) < cout "Cannot open the file to output. \n"; return; > char str[80]; cout "Save the lines to HDD. Enter ! to stop." do < cout ":"; // чтение строки с использоанием пробелов между словами cin.getline(str, 80); os // записать в файл строку str > while (*str != '!'); // конец ввода строк - ! os.close(); // закрыть файл return; > void main() < Example1("myfile.txt"); >
После вызова функции Example1() будет создан файл «myfile.txt» , в котором будут записаны строки, введенные с клавиатуры.
Результат работи программы:
Save the lines to HDD. Enter ! to stop. :Hello world! :bestprog.net :Working with files in C++ :!
2. Функция, которая читает текстовый файл и выводит его содержимое на экран
Функция Example2() читает содержимое файла filename , имя которого есть входящим параметром функции.
#include #include using namespace std; // Функция, которая читает файл filename и выводит его на экран // Используется класс ifstream для чтения файла bool Example2(const char * filename) < // создать экземпляр класса ifstream is(filename); // открыть файл в конструкторе // если файл не открыт, то выход с кодом false if (!is) return false; // чтение строк в цикле while char str[100]; // буфер для чтения одной строки while (is) // если is ненулевое, то еще не конец файла < is.getline(str, 100); // прочитать строку из файла в буфер str cout // вывести на екран > is.close(); // закрыть файл return true; > void main() < Example2("myfile.txt"); >
Результат работи программы отображает содержимое файла «myfile.txt» , созданного в п.1 этой темы
Hello world! bestprog.net Working with files in C++ !
3. Пример бесформатного ввода/вывода. Копирование одного файла в другой
В примере реализована функция Example3() , которая выполняет копирование файлов в двоичном (бинарном) формате. Функция получает два параметра. Первый параметр типа const char* есть имя файла-источника. Второй параметр типа const char* есть имя файла-назначения.
Функция реализует посимвольное копирование. Для получения символа из файла-источника используется функция get() .
#include #include using namespace std; // Бесформатный ввод/вывод, копирование одного файла в другой // Функция читает файл readFile и записывает его в файл writeFile // использует функцию get() для чтения bool Example3(const char * readFile, const char * writeFile) < // создать экземпляры классов ifstream, ofstream ifstream rf(readFile, ios::in | ios::binary); // файл-источник (чтение) // проверка, открыт ли файл-источник if (!rf) < cout "Cannot open source file." return false; > // файл-назначение (запись) ofstream wf(writeFile, ios::out | ios::binary); // проверка, открыт ли файл-назначение if (!wf) < rf.close(); // закрыть файл-источник cout "Cannot open the files." return false; > char sym; // цикл посимвольного чтения while (rf) < rf.get(sym); // считать из rf => sym if (rf) wf // записать sym => wf > cout "Copy result: OK!" // закрыть оба файла wf.close(); >
Вызов функции Example3() из функции main() может быть следующим:
void main() < Example3("myfile.txt", "myfile2.txt"); >
После выполнения функции Example3() будет создан файл «myfile2.txt» , который будет копией файла «myfile.txt» .
4. Пример бесформатного ввода/вывода. Копирование одного файла в другой. Функция put()
#include #include using namespace std; // То же, что и Example3(), только для записи использует функцию put() bool Example4(const char * readFile, const char * writeFile) < ifstream rf(readFile, ios::in | ios::binary); // проверка, открыт ли файл if (!rf.is_open()) return false; ofstream wf(writeFile, ios::out | ios::binary); // проверка, открыт ли файл if (!wf.is_open()) < rf.close(); // закрыть ранее открытый файл rf return false; > char sym; // цикл посимвольного чтения while (rf) < rf.get(sym); // считать из rf => sym if (rf) wf.put(sym); // записать sym => wf > rf.close(); // закрыть оба файла wf.close(); > void main() < Example4("myfile.txt", "myfile2.txt"); >
5. Пример функции записывающей структурную переменную в файл
По данному примеру можно реализовывать собственные функции, которые будут записывать структуры или классы в файл.
Реализована функция Example5() , которая выполняет запись структурной переменной типа BOOK в файл, имя которого есть входящим параметром. Функция Example5() использует функцию write() для записи. Файл открывается в двоичном формате ( ios::binary ).
#include #include #include using namespace std; // Функция, которая записывает структуру типа BOOK в файл, // использует функцию write() struct BOOK < char title[100]; // название книги char author[70]; // автор int year; // год выпуска float price; // стоимость книги >; bool Example5(const char * filename) < // создать структуру BOOK B; strcpy_s(B.title, "Title of book"); strcpy_s(B.author, "Authof of book"); B.year = 2000; B.price = 12.65f; // создать экземпляр файла в двоичном формате (ios::binary) ofstream outFile(filename, ios::out | ios::binary); if (!outFile) return false; // запись структурной переменной B в файл outFile.write((char*)&B, sizeof(BOOK)); outFile.close(); // закрыть файл return true; > void main() < Example5("myfile3.txt"); >
6. Пример чтения структурной переменной из файла
Данный пример есть продолжением предыдущего примера из пункта 5. В примере в функции Example6() заполняется значение структурной переменной типа BOOK . Полученное значение формируется как входящий параметр-ссылка на тип BOOK . Также функция получает параметром имя файла для чтения. Для чтения структурной переменной используется функция read() .
Результат работы программы
Array is written Read the array. Array C: Title = Title-01, Author = Author-01, Year = 2005, Price = 100.95 Title = Title-02, Author = Author-02, Year = 2008, Price = 90.25 Title = Title-03, Author = Author-03, Year = 2002, Price = 180.5
8. Пример записи/чтения массива чисел типа double
- запись в файл массива M чисел типа double функцией write() ;
- чтение из файла массива чисел типу double функцией read() .
Файл открывается в двоичном формате.
#include > #include using namespace std; // использование функций read(), write() для записи/чтения массива чисел bool Example8(const char * filename) < double M[] = < 2.44, 3.85, -3.23, 11.85, 3.38 >; // массив чисел int i; int n = 5; // количество элементов в массиве M // 1. Запись массива в файл // 1.1. Создать экземпляр outF, связанный с файлом filename ofstream outF(filename, ios::out | ios::binary); // для записи, двоичный формат // 1.2. Проверка открытия файла if (!outF) < cout "Error. Cannot open the file."; return false; > // 1.3. Записать количество элементов в массиве M outF.write((char*)&n, sizeof(int)); // 1.4. Записать весь массив в файл outF.write((char*)&M, sizeof(double)*n); outF.close(); // закрыть файл // 2. Чтение данных из файла filename в массив M2 double M2[5]; int n2; // 2.1. Открыть файл для чтения ifstream inF(filename, ios::in | ios::binary); // 2.2. Проверка, открыт ли файл? if (!inF) < cout "Error. Cannot open file."; return false; > // 2.3. Прочитать количество элементов в массиве inF.read((char*)&n2, sizeof(int)); // 2.4. Считать данные из файла в массив M2 inF.read((char*)&M2, sizeof(double)*n2); inF.close(); // закрыть файл // 2.5. Вывести массив M2 на экран cout "Array M2:\n"; for (i = 0; i < n2; i++) cout << M2[i] " "; cout return true; > void main() < Example8("file8.bin"); >
Результат работи программы:
Array M2: 2.44 3.85 -3.23 11.85 3.38
9. Пример чтения из файла строк. Функция getline()
В функции Example9() реализуется построчное чтение из файла с помощью функции getline() . Имя файла задается входящим параметром filename . Строки в файле состоят из набора слов, разделенных символом «пробел».
#include #include using namespace std; // Чтение из файла. Функция getline() bool Example9(const char* filename) < ifstream inputFile(filename); char buffer[255]; // буфер для сохранения одной строки if (!inputFile) < cout "Error. Cannot open file"; return false; > // цикл чтения строк из файла while (inputFile) < inputFile.getline(buffer, 255); if (inputFile) cout // вывод прочитанной строки на экран > inputFile.close(); return true; >
10. Пример чтения строк из файла. Функции getline() + eof()
В примере реализована функция Example10() , которая выполняет чтение строк из файла. Файл открывается в текстовом формате. Имя файла задается входящим параметром функции. Определение конца файла выполняется с помощью функции eof() .
#include #include using namespace std; // чтение из файла. Функции getline()+eof() bool Example10(const char* filename) < // создать экземпляр файла filename ifstream inputFile(filename, ios::in); if (!inputFile) return false; char buffer[255]; // буфер для сохранения одной строки // цикл чтения строк файла // строки читаются до тех пор, пока не будет достигнут конец файла while (!inputFile.eof()) < inputFile.getline(buffer, 255); if (inputFile) cout inputFile.close(); >
Связанные темы
Как записать и считать структуру в файл на СИ
Всем добрый вечер, такая проблема, записываю структуру в файл, но при следующем запуске и чтении из файла, программа перестает работать. Также при удаление строчки 114 и 115 программа перестает работать, может кто подсказать почему?
#include #include #include #include using namespace std; typedef struct _date < int num; char* month; int year; >date; typedef struct _fio < char* fam; //Фамилия char* name; //Имя char* father; //отчество >fio; struct _deposid < int countName; fio client; date date; double sum; >; // Функция вывода счета void printClient(_deposid &client) < cout void sumLarge(_deposid *clients, int thisClient, int needSum, int countSum) < for (int i = 0; i < thisClient; i++ ) < if (clients[i].sum >needSum) < printClient(clients[i]); countSum++; >> if (countSum == 0) < cout countSum = 0; > void printNameLastname(_deposid *clients, int thisClient, char *firstname, char *lastname, char *father, int countSum) < for (int i = 0; i < thisClient; i++ ) < if (strcmp(clients[i].client.name, firstname) == 0 && strcmp(clients[i].client.fam, lastname) == 0 && strcmp(clients[i].client.father, father) == 0) < printClient(clients[i]); countSum++; >> if (countSum == 0) < cout countSum = 0; > // Функция создания нового клиента void newClient(_deposid &client) < char *userLastname = new char[100], *userFirstname = new char[100], *userFather = new char[100], *userMonth = new char[100]; // Ввод данных cout > userFirstname; client.client.name = new char[strlen(userFirstname)+1]; strcpy(client.client.name, userFirstname); cout > userLastname; client.client.fam = new char[strlen(userLastname)+1]; strcpy(client.client.fam, userLastname); cout > userFather; client.client.father = new char[strlen(userFather)+1]; strcpy(client.client.father, userFather); cout > client.date.num; cout > userMonth; client.date.month = new char[strlen(userMonth)+1]; strcpy(client.date.month, userMonth); cout > client.date.year; cout > client.sum; // Очистка памяти delete [] userLastname; delete [] userFirstname; > int main() < char *lastname = new char[100], *firstname = new char[100], *father = new char[100]; int n, thisClient = 0, numberFunction = 0, needSum, countSum = 0; _deposid *clients; char c; FILE *fp; fp=fopen("t.txt", "r+"); if (fp==NULL) < fclose(fp); fp=fopen("t.txt", "w+"); cout > n; clients = new _deposid[n]; // Создание массива структур > else < while((c = fgetc(fp)) != EOF) // При удалении этой и строчки ниже программа вообще не запускается putchar(c); while(fread(clients, sizeof(_deposid),1,fp)); // fputs("lol", fp); // fclose(fp); >cout > numberFunction; switch (numberFunction) < case 1: if (thisClient == n) < cout else < clients[thisClient].countName = thisClient + 1; newClient(clients[thisClient]); cout break; case 2: cout break; case 3: // Вывод счетов с суммой большей заданной cout > needSum; cout > firstname; cout > lastname; cout > father; cout > >
Отслеживать
задан 16 мая 2022 в 11:18
егор виктуров егор виктуров
3 2 2 бронзовых знака
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
В принципе всё просто. Вы в файл сохраняете не все данные.
case 5: cout
Когда вы так пишете, в файл сохраняется сама структура. Т.е. в файл сохраняются не текстовые данные, кот. вы записывали в выделяемые через new буферы, а просто значение указателей char* month; и char* fam; char* name; char* father; . Для примера, проверьте значение sizeof(_deposid) - в размере структуры не будет размера того текста (фамилии, имени, отчества, названия месяца), которые вы вводили с клавиатуры.
А потом Вы загружаете из файла значение указателей, которые указывают в "никуда". Вам нужно написать свою функцию вывода Вашей структуры в файл. И свою функцию загрузки структуры из файла.
Существуют уже готовые библиотеки сериализации (вывода в поток и загрузки из потоков) объектов. Но для этой задачи проще написать свои функции.
Совет - каждое поле пишите отдельной строкой и считывайте построчно - так будет проще.
Ну и вот этот кусок:
fp=fopen("t.txt", "r+"); if (fp==NULL) < clients = new _deposid[n]; // Создание массива структур >else < while((c = fgetc(fp)) != EOF) // При удалении этой и строчки ниже программа вообще не запускается putchar(c); while(fread(clients, sizeof(_deposid),1,fp));
Как правильно написал @JohnDoe выделение памяти под массив структур делается только если файла не существует. Если он существует, память под массив не выделяется. И дальше у Вас 2 ошибки, первая из которых не дает состояться второй. Вторая ошибка - запись данных в невыделенную область памяти:
fread(clients, sizeof(_deposid),1,fp)
Здесь для clients память не выделена! А Вы пытаетесь записать туда что-то!
А первая ошибка не дает случиться второй - вот этими строками вы считываете весь файл.
while((c = fgetc(fp)) != EOF) putchar(c);
Дескриптор файла указывает на конец файла. Функция fread() не может ничего считать из файла и поэтому ничего не записывает в невыделенную память.
По вашей логике при записи массива структур в файл вам нужно сначала записать туда количество объектов в массиве, а потом - сами объекты. И считывать в таком же порядке:
- считать количество объектов,
- выделить под них память,
- считать сами объекты
Ещё один момент:
void newClient(_deposid &client) < char *userLastname = new char[100], *userFirstname = new char[100], *userFather = new char[100], *userMonth = new char[100]; . delete [] userLastname; delete [] userFirstname;
Выделили 4 массива, а освободили только 2. А в main() выделили и вообще не освободили. И зачем выделение через new() , если вы знаете размер буферов? Только чтобы память на куче была выделена? Стек выдержит ваши 400 байт, сделайте проще:
void newClient(_deposid &client)