Как считать структуру из файла c
Регистрация: 23.10.2016
Сообщений: 86
Чтение структуры с файла и её сортировка на Си
Вообщем дали мне задание:
Считать с текстового файла Фамилии 25 студентов и улицу их проживания. И вывести на экран данные тех студентов которые живут на одной улице. Насколько я понял надо использовать структуры.
Сразу предупреждаю что я компилирую через Clang на линуксе и он ругается на gets\fgets, так что просьба такого не предлагать.
Я Создал файл со сгенерироваными фамилиями и улицами, и теперь не знаю как считать их оттуда, потому что если пробую через scanf то в консоль выводит по половине фамилии и улицы причём вперемешку (тоесть фамилия не совпадает с улицей). Соответственно нужна помощь в обяснении как считать с текстового файла данные в структуру и потом отсортировать их по названию улицы. Ну и если кто знает, то подскажите как сделать так чтобы Clang не ругался. Буду рад любому совету.
Вот файл с фамилиями:
| Subbotina Patona Kalashnikov Lichakivska Subbotin Puluja Ustinova Kulparkivska Kudrjavcev Bandery Merkusheva Saharova Silina KnjahyniOlhy Petuhov Naukova Gerasimova VolodymyraVelykhogo Ponomarjova Naukova Grigoreva Puluja Matvienko Chornovola Lukin Patona Medvedev Himichna Kuznecov Pekarska Kabanov Gorodotska Pavlov Lubinska Bogdanov Antonovicha Stepanova Symonenka Bogdanova Vugovskogo Markov Stryjska Bragin Kopernyka Bespalova Khmelnytskogo Evdokimova Sadova Chernova Himichna |
Насылаю баги по фотографии
Как считать структуру из файла c
Хотя функции getc()/putc() позволяют вносить в файл отдельные символы, но фактически мы имеем дело с бинарными файлами. Если мы записываем в файл строку, то в принципе мы даже можем открыть записанный файл любом текстовом редакторе и понять, что там было записано. Но не всегда данные могут представлять строки. И чтобы более наглядно разобраться с работой с бинарными файлами, рассмотрим еще одни пример — с записью-чтением структуры из файла.
#include #include struct person < char name[16]; int age; >; int save(char * filename, struct person *p); int load(char * filename); int main(void) < char * filename = "person.dat"; struct person tom = < "Tom Smith", 21 >; save(filename, &tom); load(filename); return 0; > // запись структуры в файл int save(char * filename, struct person *p) < FILE * fp; char *c; int size = sizeof(struct person); // количество записываемых байтов fp = fopen(filename, "wb"); //открываем файл для бинарной записи if (!fp) // если не удалось открыть файл < printf("Error occured while opening file \n"); return 1; >// устанавливаем указатель на начало структуры c = (char *)p; // посимвольно записываем в файл структуру for (int i = 0; i < size; i++) < putc(*c++, fp); >fclose(fp); return 0; > // загрузка из файла структуры int load(char * filename) < FILE * fp; char *c; int i; // для считывания одного символа // количество считываемых байтов int size = sizeof(struct person); // выделяем память для считываемой структуры struct person * ptr = malloc(size); fp = fopen(filename, "rb"); // открываем файл для бинарного чтения if (!fp) < printf("Error occured while opening file \n"); return 1; >// устанавливаем указатель на начало блока выделенной памяти c = (char *)ptr; // считываем посимвольно из файла while ((i = getc(fp))!=EOF) < *c = i; c++; >fclose(fp); // вывод на консоль загруженной структуры printf("%-20s %5d \n", ptr->name, ptr->age); free(ptr); return 0; >
В данном случае запись и чтение структуры выделены в отдельные функции: save() и load() соответственно.
Для записи в функции save() через параметр struct person *p получаем указатель на сохраняемую структур. Фактически его значением является начальный адрес блока памяти, где располагается структура.
Функция putc записывает отдельный символ в файл, однако нам надо записать структуру. Для этого мы создаем указатель на символ (который по сути представляет один байт) и устанавливаем этот указатель на начало блока памяти, выделенного для структуры.
c = (char *)p;
То есть в данном случае мы получаем адрес в памяти первого байта из блока памяти, которая выделена для структуры. И затем мы можем пройтись по всему этому блоку и получить отдельные байты и занести их в файл:
for (int i = 0; i
И в данном случае нам не важно, какие поля имеет структура, какой она имеет размер. Мы работаем с ней как с набором байт и заносим эти байты в файл. После занесения каждого отдельного байта в файл указатель c в блоке памяти перемещается на один байт вперед.
При чтении файла в функции load() используется похожий принцип только в обратную сторону.
Во-первых, для считывания структуры из файла мы выделяем блок динамической памяти для хранения прочитанных данных:
struct person * ptr = (struct person *) malloc(size);
После этого указатель ptr будет указывать на первый адрес блока из 20 байт (наша структура занимает 20 байт = 16 символов и 4 байта для числа int ).
Затем так как при прочтении мы получаем символы, устанавливаем указатель на первый байт выделенного блока и в цикле считываем данные из файла в этот блок:
c = (char *)ptr; // считываем посимвольно из файла while ((i = getc(fp))!=EOF)
Здесь стоит обратить внимание на то, что в данном случае на самом деле считываем даже не символ, а числовой код символа в переменную типа int и только потом передаем значение указателю c. Это сделано для корректной обработки окончания файла EOF. Это значение может представлять любое отрицательное число. И если бы мы сохранили отрицательное число (например, возраст пользователя был бы отрицательным), то оно было бы некорректно интерпретировано при чтении как конец файла, и итоговый результа был бы неопределенным. Поэтому более правильно считывать именно числовой код символа в переменную int, а затем числовой код передавать в char.
Запись и чтение массива структур
Выше приведен пример по работе с одной структурой. Но, как правило, при работе с файлами мы оперируем не одной структурой, а каким-то набором структур. Поэтому усложним задачу и сохраним и считаем из файла массив структур:
#include #include struct person < char name[20]; int age; >; int save(char * filename, struct person *st, int n); int load(char * filename); int main(void) < char * filename = "people.dat"; struct person people[] = < , , , >; int n = sizeof(people) / sizeof(people[0]); save(filename, people, n); load(filename); return 0; > // запись в файл массива структур int save(char * filename, struct person * st, int n) < char *c; // для записи отдельных символов // число записываемых байтов int size = n * sizeof(struct person); FILE * fp = fopen(filename, "wb"); if (!fp) < printf("Error occured while opening file\n"); return -1; >// записываем количество структур c = (char *)&n; for (int i = 0; i < sizeof(n); i++) < putc(*c++, fp); >// посимвольно записываем в файл все структуры c = (char *)st; for (int i = 0; i < size; i++) < putc(*c, fp); c++; >fclose(fp); return 0; > // загрузка из файла массива структур int load(char * filename) < char *c; // для считывания отдельных символов int m = sizeof(int); // сколько надо считать для получения размера массива int n; // количество структур в массиве FILE * fp = fopen(filename, "r"); if (!fp) < printf("Error occured while opening file\n"); return -1; >// выделяем память для хранения количества данных int *ptr_count = malloc(m); // считываем количество структур c = (char *)ptr_count; // пока не считаем m байт, сохраняем байт в выделенный блок для размера массива while (m > 0 && (*c = getc(fp)) != EOF) < c++; m--; >//получаем число элементов n = *ptr_count; free(ptr_count); // освобождаем память // выделяем память для считанного массива структур struct person * ptr = malloc(n * sizeof(struct person)); // устанавливаем указатель на блок памяти, выделенный для массива структур c = (char *)ptr; // считываем посимвольно из файла while ((*c= getc(fp))!=EOF) < c++; >// перебор загруженных элементов и вывод на консоль printf("\nFound %d people\n\n", n); for (int i = 0; i < n; i++) < printf("%-5d %-10s %5d \n", i + 1, (ptr + i)->name, (ptr + i)->age); // или так // printf("%-5d %-10s %5d \n", i + 1, ptr[i].name, ptr[i].age); > free(ptr); fclose(fp); return 0; >
Данная задача усложнена тем, что нам надо хранить массив структур, количество которых точно может быть неизвестно. Один из вариантов рещения этой проблемы состоит в сохранении некоторой метаинформации о файле в начале файла. В частности, в данном случае в начале файла сохраняется число записанных структур.
Запись во многом аналогична записи одной структуры. Сначала устанавливаем указатель на число n , которое представляет количество структур, и все байты этого числа записываем в файл:
c = (char *)&n; for (int i = 0; i
Затем подобным образом записываем все байты из массива структур — устанавливаем указатель на первый байт массива структур и записываем size байт в файл:
// посимвольно записываем в файл все структуры c = (char *)st; for (int i = 0; i
При чтении нам придется файктически считывать из файла два значения: количество структур и их массив. Поэтому при чтении два раза выделяется память. Вначале для количества элементов:
int *ptr_count = malloc(m);
Затем мы считываем первые 4 байта из файла для получения числа:
c = (char *)ptr_count; while (m > 0 && (*c = getc(fp)) != EOF) < c++; m--; >//получаем число элементов n = *ptr_count;
Затем аналогичные действия проделываем для массива структур.
struct person * ptr = malloc(n * sizeof(struct person)); // устанавливаем указатель на блок памяти, выделенный для массива структур c = (char *)ptr; // считываем посимвольно из файла while ((*c= getc(fp))!=EOF)
И результатом программы должен быть вывод считанных данных:
Found 4 people 1 Tom 23 2 Alice 27 3 Bob 31 4 Kate 29
Как считывать структуру из файла?
ну вы все так делаете. и да выше вам это уже посоветовали, работайте с бинарным пердставлением так проще в том случае если вы работаетте с файлом только в программе. Ну единственное замечание передавайте свою структуру как парметр для функции записи и чтения, а не объявляйте ее локально иначе все, что вы читаете доступно только в вашей функции.
21 мар 2017 в 11:11
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Вот ваша структура
struct School < unsigned long mark; char subject[20]; char surname[20]; short age; >;
Вот так лучше писать и читать
void setInfo(School& school) < ofstream fout("out.txt", ostream::binary); //инициализация потока файла //Заполнили структурку cout > school.mark; cout > school.subject; cout > school.surname; cout > school.age; //записали fout.write((char*)&school, sizeof(School)); fout.close(); > void getInfo(School& school) < ifstream fin("out.txt", ios::in); while (!fin.eof()) < fin.read((char*)&school, sizeof(School)); >fin.close(); > int main() < School school; //так пишем setInfo(school); //так читаем getInfo(school); >
это что касается считывания из файла , далее для работы с множественными объектами струтктуры необходио использовать контейнеры, напрмиер vector или list .
вот пример функции которая запишет в файл все ваши объекты
void recAllSchoolObj(vector vSchool) < ostream fout("out.txt", ostream::binary) for(int i =0; ifout.close(); >
вот пример который прочитает
void readAllSchoolObj(vector vSchool) < School school; istream fin("out.txt"); while(!fin.eof) < fin.read((char*)&school, sizeof(School)); vSchool.push_back(school); >fin.close; >
поиск всех фамилий с одной оценкой
void findMark(vector vSchool, unsigned long mark) < cout>
Как считать структуру из файла c
Интуитивное определение файла звучит примерно так. Файл — именованная область на жестком диске. На самом деле с точки зрения ОС UNIX это совсем не так. В ОС UNIX файл — очень удобная абстракция. С точки зрения UNIX файлом называется «что-нибудь», из чего можно считывать информацию или во что можно записывать информацию. Файлы это:
- Файлы в обычном смысле: файлы, которые хранятся на жестком диске (можно считывать из них и запиcывать в них информацию);
- Экран монитора: файл, в который можно выводить информацию (отобразится на экране монитора);
- Клавиатура: файл, из которого можно считывать информацию;
- Принтер: файл, в который можно выводить информацию (печать текста);
- Модем: файл, из которого можно считывать информацию и в который можно записывать информацию (обмен информации по сети);
0.2 Разделение понятий файла и названия
Неправильно думать, что между сущностями «файл» и «название файла» есть взаимно однозначное соответствие.
Можно привести аналогию из жизни: если представить, что файл — это банка с некоторым содержимым, то название файла — это этикетка на этой банке. Логично предположить, что у банки может быть несколько этикеток.
С точки зрения UNIX:
Правильно говорить, что у названия есть файл. И наоборот: неправильно говорить, что у файла есть название. Никакого эффективного способа узнать имя файла не существует (но можно перебрать все файлы файловой системы).
0.3 Функции link и unlink
Пусть есть файл file1.txt . Для его удаления используется функция:
int unlink(const char* filename);
Эта функция не всегда удаляет файл (с жесткого диска), а только удаляет «этикетку» этого файла. Если есть другая «этикетка» этого файла, то файл останется на жестком диске; просто у него уже не будет этой «этикетки». Файл знает, сколько у него таких «этикеток» (есть специальный счетчик). И если этот счетчик стал равен нулю, то функция удаляет файл с жесткого диска. В ОС Windows эта функция всегда удаляет файл.
Парная функция к этой функции:
int link(const char* filename1, const char* filename2);
Эта функция создает еще одну «этикетку» для этого файла и прибавляет к значению счетчика «этикеток» единицу.
1 Ввод и вывод, язык C, структура FILE
1.1 Чтение и запись: printf и scanf
Всем хорошо известная функция printf :
printf(«Hello!») — печать текста на экран;
printf(«N = %d», N) — форматированный вывод на экран: вывести число N в десятичной записи;
printf(«N = %x», N) — форматированный вывод на экран: вывести число N в шестнадцатеричной записи;
Аналогично парная функция scanf :
char *ptr = new char[10];
scanf(«%s», ptr); — считывание с клавиатуры строки в массив *ptr
char *ptr = new char[10];
scanf(«%s», ptr);
Тут налицо потенциальная проблема переполнения буфера (в данном примере в буфере всего 10 байт).
Никогда не следует пользоваться scanf ‘ом для чтения строк.
1.2 Чтение и запись файлов: FILE* , fopen , fprintf , fscanf
Есть несколько способов работы с файлами c использованием языков C и C++.
Самый распространенный связан со структурой FILE (это не класс, потому что сущность языка C). Эта структура определена в заголовочном файле стандартной библиотеки . Размер этой структуры и ее поля зависят от ОС и от версии компилятора. Поэтому никто не пользуется структурой FILE . Обычно пользуются указателем на эту структуру: FILE* . Например:
FILE *f = fopen(«file1.txt», «r»);
- не существует файла;
- у программы недостаточно прав доступа для работы с файлом;
Для дальнейшей корректной работы следует писать примерно такой код:
if (f == NULL) // файл не удалось открыть
>
else // Работа с файлом
>
Допустим, что нам удалось открыть файл, т.е. f != NULL . Тогда для того, чтобы считывать файл, можно использовать функцию:
Эта функция работает аналогично функции scanf . Поэтому использовать эту функцию небезопасно! Все проблемы, перечисленные для scanf ‘а, имеют место и при работе с fscanf ‘ом.
Если мы хотим записать в файл что-то, то мы должны сначала открыть его на запись:
FILE *f = fopen(«file2.html», «w»);
Тут «w» означает, что мы открываем файл на запись (от write). Если файл не существовал, то он создастся и откроется на запись, а если он существовал, то он сначала будет уничтожен, а затем создан заново, и потом файл будет открыт на запись.
Еще один способ открыть файл — это открыть его на дозапись. Это можно сделать с помощью параметра «a» (от append). Если файл не существовал, то он создастся и откроется на запись, а если он существовал, то он откроется на запись, и запись будет производится в конец файла.
Затем можно использовать функцию fprintf(f, . )
1.2.1 Зачем нужно закрывать файлы
- Зададимся вопросом: «Что надо сделать после того, как мы поработали с файлом?»
Формальный ответ: «Закрыть файл.» Это можно сделать с помощью функции:
fclose(f);
Но зачем это делать?
Ввиду механического устройства жесткого диска, данные в файл попадают не сразу. Сначала данные записываются в так называемый буфер (область оперативной памяти), и когда он переполнится, то данные из буфера будут записаны в файл. Такая схема придумана для ускорения работы с файлами. На самом деле, буфер — это поле структуры FILE : указатель на массив char ‘ов.
1.2.2 Важность буфера при работе с файлами
- Рассмотрим следующую ситуацию. Программа пишет протокол своих действий в файл (например, с помощью функции fprintf ). Допустим, что программа сломалась. Понятно, что скорее всего получится так, что в файл последний fprintf (последний протокол действий) не запишется. Причина тому — это буфер.
Чтобы «протолкнуть» буфер в файл, используется функция
В коде это выглядит примерно так:
1.3 Стандартные уже открытые файлы: stdin, stdout, stderr
С точки зрения UNIX клавиатура и экран — это файлы.
Есть три стандартные константы:
FILE *stdin
FILE *stdout
FILE *stderr
Это три стандартных заранее открытых файла.
stdin — это стандартный файл (поток) ввода, а stdout — стандартный файл (поток) вывода. Таким образом:
scanf(. ) в точности эквивалентно fscanf(stdin, . )
printf(. ) в точности эквивалентно fprintf(stdout, . )
Такой гибкостью можно воспользоваться при написании программы для работы с файлами. Например, для отладки программы можно выводить информацию на экран монитора, а не в файл. Для этого в начале работы с файлом пишем две строчки:
//FILE *f = fopen(. );
FILE *f = stdin;
При этом код программы будет содержать такие функции: fscanf(f, . ) или fprintf(f, . ) . А когда отладка законичится, просто снимаем/ставим соответствующие комментарии в двух строчках программы.
stderr — это стандартный файл (поток) ошибок. По умолчанию выводит данные на экран.
Но существует заметное отличие этого «файла» от stdin и stdout : stderr — небуферизованный файл (поток). Поэтому в этот файл (поток) все байты уходят без «задержки», которая могла бы возникнуть при буферизированном подходе. Понятно, что польза от этого подхода заключается в том, что вместо кода:
1.4 Текстовые и бинарные файлы; что меняет опция t/b
fopen(f, «file1.txt», «w»);
Почему второй параметр «w» является строкой, а не символом?
На самом деле бывает много способов прочитать/записать файл. Например:
fopen(«file1.txt», «wt») — откроет файл как текстовый файл;
fopen(«file1.txt», «wb») — откроет файл как бинарный файл.
Но в чем отличие?
Разница заключается лишь в том, что символы переноса строк запишутся по разному.
Рассмотрим пример в UNIX и Windows:
Исходная строка кода выглядит так: fprintf(«Hello\n»);
-
Откроем в Windows файл на запись с параметром «wb» (как бинарный файл). Это означает, что в него запишется в точности то, что мы передали в функции fprintf . Тогда в файл запишутся ровно 6 байт: Hello\10
1.5 Как же читать/писать на самом деле: fgets , fread и fwrite
Использование функций ptintf и scanf для записи и для чтения — это очень плохая идея. Тогда все-таки как лучше читать и записывать?
Хороший способ чтения из файла дает функция fgets() (от «get string»):
char *fgets(char *buffer, size_t length, FILE *file);
- buffer — это указатель на буфер, в который мы читаем;
- length — это размер буфера;
- file — это файл, из которого мы читаем (если читаем с клавиатуры, то разумно использовать stdin ).
- Функция возвращает строку
Налицо быстрота и безопасность. Главное отличие от scanf ‘а заключается в том, что функция перестанет читать в тот момент, когда закончится буфер. Быстрота обусловлена тем, что функция scanf должна в момент выполнения разобрать форматную строку, в то время как fgets просто читает строку.
1.5.1 Как доставать числа? Семейство atoi, sscanf
В то время как фунция fgets читеат обычную строку, функция scanf может читать и различные другие типы (целые, вещественные числа).
В языке C есть семейство функций ~ atoi (a — ASCII , i — integer ):
Функция принимает единственный параметр строку и пытается ее привести в типу int . Надо заметить, что функция atoi безопасная, но не очень удобная. Безопасная в том смысле, что не сломается: atoi(«25a») == 25 . «Неудобства» заключаются в том, то если мы передаем в качестве параметра строку, в которой есть не только числа, нужно быть очень внимательным и знать, как работает эта функция. Функция atoi никак не проинформирует нас, если преобразование прошло неудачно.
Например, atoi(«abc») == 0 , что на самом деле не совсем соостветствует действительности. Использовать функцию atoi нужно лишь в том случае, когда вы уверены, что в строке есть число.
Родственные функции: atol, atoll, atof, strtol .
Им соответствуют функции для преобразования в типы long , long long и float .
Рассмотрим подробнее strtol :
long strtol(char *buffer, char **endPtr, int base);
- buffer — это указатель на буфер, из которого мы читаем;
- endPtr — это переменная, которая используется для того, чтобы сообщить нам насколько успешно произошло преобразование;
это указатель на char * , в котором записан первый символ, который не смог проинтерпретироваться с помощью функции strtol ;
Применение выглядит примерно так:
char *end;
char *ptr = «25a»;
int N = strtol(ptr, &end, 10);
Теперь end указывает на «a».
if (ptr == end) <
// ничего не получилось прочитать
>
Более мощное средство
Есть более мощное средство, чем нежели fgets + atoi . Речь идет о функции sscanf .
Вместо использования функции fscanf(f, «%d», &N) можно использовать связку:
fgets(ptr, 100, f);
sscanf(ptr, «%d», &N);
В чем преимущество и мощность такого подхода?
- Мы знаем длину того, что мы прочитали;
- Рассмотрим следующую ситуацию: мы хотим прочитать какие-то данные, но не смогли из-за ошибки.
Используя такой подход, мы можем передать пользователю сообщение об ошибке и ту строчку, которую нам не удалось прочитать: ptr . С использованием fscanf ‘а это невозможно.
1.5.2 fread и fwrite
На самом деле не все файлы выглядят как текст. Файле могут быть записаны числовые данные.
size_t fread(void *ptr, size_t size, size_t nelts, FILE *f);
- void *ptr — указатель на ту область памяти, в которую мы читаем;
- size_t size — размер элемента, который мы читаем;
- size_t nelts — максимальное количество элементов, которые можно записать;
- FILE *f — файл, из которого читаем;
- size_t fread() — сама функция возвращает количество элементов, которые удалось прочитать.
size_t fwrite(const void *ptr, size_t size, size_t nelts, FILE *F);
Аналогично fread эта функция возвращает количество элементов, которые удалось записать.
Тут параметр nelts просто показывает, сколько элементов надо вывести.
1.6 Другие полезные опции: fseek и ftell
Для файлов, которые открыты на чтение есть полезные функции. Одна из них это:
int fseek(FILE *f, long offset, int flag);
- FILE *f — файл, в котором передвигаемся;
- long offset — количество байтов для отступа, отступ производится в соответствии с 3-м параметром;
- int flag — позиция, от которой будет совершен отступ; в стандартной библиотеке C для этого параметра определены 3 константы:
SEEK_SET — начало файла;
SEEK_CUR — текущас позиция;
SEEK_END — конец файла;
long int ftell(FILE *f);
2 Другие подходы для работы с файлами
2.1 File descriptors. Open, close, read, write
В языке C есть много способов работы с файлами. Помимо структуры FILE можно использовать так называемые дескрипторы файла (file descriptors). Дескриптор файла — целое неотрицательное число. Оно обозначает номер открытого файла в таблице открытых файлов операционной системы. Использование дескрипторов файла — более низкий уровень, чем нежели ипользование струкруты FILE. Структура FILE — сущность языка C и его стандартной библиотеки, тогда как дескриптор файла — сущность операционной системы. Например, при работе со структурой FILE автоматически создается буфер, и программист работает с более высокоуровневой абстракцией. А при работе с дескрипторами файла программист должен позаботится о буферизации вручную.
Пример работы с дескрипторами файла довольно прост и почти в точности повторяет процесс работы со структурой FILE:
Сходство работы с дескрипторами файла с работой со структурой FILE заключается в том, что в названии функций отсутствует буква «f» . Иногда параметры функций незначительно отличаются.
Структуру FILE полезно использовать при работе с настоящими «файлами» (которые находятся на жестком диске). Ипользовать дескрипторы файла полезно в случаях работы со специальными «файлами». В этом подходе есть своя специфика работы, но сейчас просто полезно знать, что такой подход существует.
Аналогами stdin, stdout и stderr в дескрипторах файла являются числа 0, 1 и 2 соответственно. Стандарт POSIX.1 обозначил числа 0, 1, 2 символическими константами STDIN_FILENO, STDOUT_FILENO и STDERR_FILENO соответственно.
2.2 Memory mapping. Что делает mmap
Следующий способ работы с файлами удобен в тех случаях, когда приходится читать файл нелинейно: надо «ходить» вперед и назад. В предыдущих подходах такие ситуации оказывались неудобными с точки зрения программирования: получился бы громоздкий код.
В языке C был придуман удобный способ работы в таких ситуациях, который называется memory mapping. Соответствующая функция:
Работает эта функция примерно так. Мы указываем этой функции файл на диске, и она «отображает» этот файл в такую-то область в памяти. В результате работы функции мы получаем указатель на начало файла. И потом мы можем работать с этим файлом как с обычным указателем на какую-то область памяти: можем «ходить» вперед и назад по этому файлу.
Можно «отобразить» не весь файл целиком, а, например, отдельную часть файла: с 3-его килобайта по 4-ый килобайт.
2.3 Win32 API: FileCreate, FileRead , etc.
При работе с файлами в ОС Windows можно использовать все те функции, которые были описаны выше. В ОС Windows есть своя большая стандартная библиотека Win32 API . В этой библиотеке также есть функции для работы с файлами: например, функции FileCreate(. ) или FileOpen(. ) . Они по своей работе похожи на функции из стандартной библиотеки C, но отличия также присутствуют. Они заключаются в параметрах этих функций и небольших «хитростях», которые мы здесь опустим.
Если вы программируете под ОС Windows и пишите программу для работы в ОС Windows, то стоит пользоваться библиотекой Win32 API для работы с файлами.
3 Ввод и вывод в языке C++, потоки
В языке C++ объекты для работы с файлами называются потоками (streams). В данном случае слово «поток» означает то же самое, что и «файл» в языке C.
Классы для работы с файлами в языке C++ называются std::istream и std::ostream для ввода и вывода соответственно.
3.1 Глобальные переменные std::cout, std::cin, std::cerr
В header’е
В этом классе есть перегруженный оператор
В данном случае на экран будет выведена единица.
Еще в header’е объявлены переменные std::cin и std::cerr для стандартного потока ввода и потока ошибок соответственно. Они являются объектами классов std::istream и std::ostream соотсветственно.
Аналогично тому, как stderr отличается от stdin , в языке C++ std::cerr отличается от std::cout отсутствием буферизации.
В классе std::istream есть перегруженный оператор >>. Можно считывать информацию из стандартного потока ввода (с клавиатуры).
3.2 Форматированный вывод возможен: std::ios::hex
Возможен ли форматированный вывод, которым мы пользовались в языке C фунцией printf() ? Наример, как вывести ту же переменную N в 16-ой записи?
В языке C++ форматированный вывод возможен при помощи вывода на экран специальной управляющей команды:
В точности то же самое выведет команда printf(«%x», N);
Чтобы не писать перед кажой переменной ее формат, можно использовать функцию:
Она установит формат вывода в стандартный поток вывода на экран. Этот подход настолько же мощный, как и использование форматной печати с помощью printf .
3.3 Операторы >
- &os — поток, в который мы будем выводить;
- N — переменная, которую мы будем выводить;
- Оператор возвращает ссылку на поток, в результате чего можно писать так: std::cout
В случае оператора >> все аналогично.
Если у нас есть класс комплексных чисел Complex , то вывод этих чисел через оператор
3.4 Работа с файлами
Классами для работы с файлами в языке C++ являются ifstream, ofstream и fstream .
Код для открытия файла и его чтения выглядит примерно так:
ifstream ifs;
ifs.open(«file1.txt»);
// далее с помощью оператора >> можно читать из файла, если он успешно открылся;
Аналогично можно использовать конструктор с параметром: ifs(«file1.txt»); после чего создается объект и открывается по возможности файл.
В классе istream есть метод close() , который закрывает файл (на подобие работы с файлами в языке C). Однако вызывать этот метод необязательно. Дело в том, что в деструкторе класса этот метод вызовется автоматически.
Работа с объектами классов ofstream и ofstream и fstream осуществляется по аналогичному сценарию.
3.5 Класс stringstream
Класс stringstream наследуется от iostream .
Используется этот для класс для следующих целей. Если мы хотим выводить комплексные числа не только на экран или в файл, но и в окно какой-нибудь программы (GUI), то как использовать stringstream ? При этом мы не хотим писать один и тот же код программы.
Можно просто печатать в строку с помощью stringstream .
3.6 Иерархия классов
4 Общий совет
Общий совет заключается в том, что не надо смешивать техники для работы с файлами.
Например, не надо в одной и той же программе использовать функции из стандартной библиотеки C ( fread / fwrite ) и классы-потоки из языка C++ ( istream / ostream ).