Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на C / C++ / Лекции по C++ ОККТ "Сервер" [22].pdf
Скачиваний:
125
Добавлен:
02.05.2014
Размер:
681.8 Кб
Скачать

Одесский колледж компьютерных технологий “СЕРВЕР”

two three

Работа с файлами

Язык С кроме стандартного ввода данных с клавиатуры и вывода результатов на экран предоставляет также возможность обмена при операциях ввода/вывода с внешними устройствами, в том числе, с файлами на диске.

ВС не предусмотрены никакие предопределённые структуры файлов (такие как файлы последовательного или прямого доступа): все файлы рассматриваются как последовательности, потоки байтов.

Для файлов определён маркер (указатель чтения/записи). Он определяет текущую позицию, к которой осуществляется доступ.

С началом работы любой программы автоматически открываются некоторые стандартные потоки, например, стандартный ввод (его имя - stdin)

истандартный вывод (его имя - stdout). По умолчанию они связаны с клавиатурой и экраном терминала соответственно. Поэтому в тех функциях ввода/вывода, которые использовались до сих пор, не указывалось, из какого потока берутся или куда помещаются данные: это известно по умолчанию.

Однако возможно в своей программе открывать другие потоки, связывать их либо с файлами на диске, либо с физическими устройствами (например, принтером), записывать в них или считывать из них информацию. Для этого служат функции ввода/вывода верхнего уровня.

Доступ к потоку осуществляется с помощью указателя. Указатель на файл описывается следующим образом:

FILE *fp;

Тип FILE – это структура, определённая в <stdio.h> и содержащая некоторую информацию о файле: например, флаги состояния файла, размер буфера, указатель на буфер и др. Описанный указатель можно связать с конкретным файлом в момент открытия данного файла. Это осуществляется с помощью функции:

fopen (“путь к файлу”,”тип доступа”),

которая возвращает указатель на файл или NULL в случае ошибки. Например, в результате выполнения оператора

fp = fopen (“ex1.txt”,”w”),

файл ex1.txt открыт для записи, а в программе на этот файл можно сослаться с помощью указателя fp (то есть функция fopen() берёт внешнее представление файла – его физическое имя – и ставит ему в соответствие внутреннее логическое имя, которое далее и будет использоваться в программе).

Вкачестве типа доступа могут быть указаны следующие параметры: “w” – существующий файл открывается для записи, при этом его

старое содержимое затирается. Маркер файла указывает на его начало. Если файл ещё не существовал, то он создаётся (если это возможно);

19

Одесский колледж компьютерных технологий “СЕРВЕР”

“r” – существующий файл открывается для чтения (с начала). Если файл не существует – это ошибка;

“a” – файл открывается для дозаписи в конец. Если файл ещё не существует, он будет создан;

“w+” – открывается пустой файл для чтения и записи, при этом его старое содержимое затирается. Если файл ещё не существовал, то он создаётся;

“r+” – существующий файл открывается для чтения и записи. Если файл не существует – это ошибка;

“a+” – файл открывается для чтения и добавления информации в ко-

нец.

Файл можно открыть в двоичном (b) или текстовом (t) режиме. Эти символы записывают во втором параметре, например, “rb” или “rt”. Двоичный режим означает, что символы перевода строки и возврата каретки (0х13 и 0х10) обрабатываются точно так же, как и остальные. В текстовом режиме эти символы преобразуются в одиночный символ перевода строки. По умолчанию файлы открываются в текстовом режиме.

Пример:

FILE *f = fopen (“d:\\cpp\\data”, “rb+”);

(открывается файл d:\cpp\data для чтения и записи в двоичном режиме и связывается с указателем f).

Указатель f используется в дальнейших операциях с потоком (файлом). При открытии потока с ним связывается область памяти, называемая буфером. При выводе вся информация направляется в буфер и накапливается там до заполнения буфера или до закрытия потока. Чтение осуществляется блоками, равными размеру буфера, и данные читаются из буфера. Буферизация позволяет более быстро и эффективно обмениваться информацией с внешними устройствами. С помощью функций setbuf и setvbuf можно управлять размерами и наличием буферов.

По окончании работы с файлом он должен быть закрыт. Для этого используется функция

fclose (указатель_файла)

Для работы с файлами используются следующие функции:

fprintf - форматированный вывод в файл, связанный с потоком. При этом файл должен быть открыт как текстовый, в режиме, допускающем запись. Синтаксис:

int fprintf (FILE *Поток, Формат, СписокПеременных);

fscanf – форматированное чтение значений переменных из файла, связанного с потоком. Файл должен быть открыт как текстовый в режиме, допускающем чтение. Синтаксис:

int fscanf (FILE *Поток, const char* Формат, СписокАдр);

20

Одесский колледж компьютерных технологий “СЕРВЕР”

fgets – читает из потока символы и записывает их в строку. Чтение заканчивается, если прочитан символ с номером КолСимволов-1 или если очередной символ является символом новой строки. Прочитанный из файла символ новой строки заменяется нулевым символом. Синтаксис:

char* fgets (char * Строка, int КолСимволов, FILE *Поток);

fputs – записывает в поток строку символов. Символ конца строки, нуль-символ, в поток не записывается. Синтаксис:

char* fputs (char * Строка, FILE *Поток);

feof – возвращает нулевое значение, если в результате выполнения последней операции чтения из потока достигнут конец файла. Синтаксис:

int feof (FILE *Поток);

Все эти функции описаны в заголовочном файле <stdio.h>.

Как было сказано ранее, файл может быть открыт в текстовом или двоичном (бинарном) режиме. В отличие от текстового файла, в бинарном можно хранить структуры в их нормальном виде. Преимущество бинарных файлов состоит в том, что при чтении/записи не тратится время на преобразование данных из символьной формы представления во внутреннюю и обратно, при этом не происходит потери точности вещественных чисел. Кроме того, при работе с бинарными файлами применяется прямой доступ к информации путём установки текущей позиции указателя. Это даёт возможность быстрого получения и изменения отдельных данных файла.

Чтение/запись в бинарный файл выполняется с помощью функций библиотеки fread и fwrite:

size_t fwrite(const void *p, size_t size, size_t n, FILE *f);

Функция fwrite записывает n элементов длиной size байт из буфера, заданного указателем p, в поток а. Возвращает число записанных аргументов.

size_t fread(void *p, size_t size, size_t n, FILE *f);

Функция fread считывает n элементов длиной size байт в буфер, заданный указателем p, из потока f. Возвращает число считанных элементов, которое может быть меньше, чем запрошенное.

Пример работы с файлами:

Допустим, что в файле хранятся сведения о мониторах. В каждой строке указан тип, оптовая и розничная цены и примечание. Для простоты данные в каждой строке записаны единообразно: первые 20 символов занимает тип монитора, далее по 5 символов целые числа, представляющие оптовую и розничную цены, затем примечание длиной не более 40 символов.

Приведенная ниже программа построчно считывает данные из текстового файла в буферную переменную s, затем формирует из них структуру mon и записывает её в двоичном режиме в выходной файл. Далее иллюстрируется считывание из этого файла произвольной записи.

#include <iostream.h> #include <stdio.h>

21

Одесский колледж компьютерных технологий “СЕРВЕР”

#include <stdlib.h> #include <string.h> int main(){

FILE *fi, *fo;

if ((fi = fopen (“d:\\c\\file.txt”, “r”)) == 0){

cout << “Ошибка открытия входного файла”; return 1;} if ((fo = fopen (“d:\\c\\binfile.txt”, “w+b”)) == 0){

cout << “Ошибка открытия входного файла”; return 1;} const int d1 = 80;

char s[d1]; struct{

char type[20]; int opt, rozn; char comm [40];

} mon;

 

int kol = 0;

//Количество записей в файле

while(fgets(s, d1, fi)){ //Преобразование строки в структуру: strncpy(mon.type, s,19);

mon.type[19] = ‘\0’; mon.opt = atoi(&s[20]); mon.rozn = atoi(&s[25]);

strncpy (mon.comm, &s[30], 40); fwrite (&mon, sizeof mon, 1, fo); kol++;

}

fclose(fi);

int i; cin >> i; //Номер записи

if (i >= kol){cout <<”Запись не существует”; return 1;} //Установка указателя текущей позиции файла на запись i: fseek(fo, (sizeof mon)*i, SEEK_SET);

fread(&mon, sizeof mon, 1,fo);

cout<<“mon.type “<<mon.type<<”opt “<<mon.opt<<”rozn ” <<mon.rozn<<endl;

fclose(fo); return 0;

}

В С++ библиотека <iostream.h> поддерживает чтение и запись в файл. Все операции, применимые к стандартному вводу/выводу, могут быть применены к файлам. Чтобы использовать файл для ввода или вывода, необходимо включить в программу заголовочный файл <fstream.h>.

22

Одесский колледж компьютерных технологий “СЕРВЕР”

Использование файлов в программе предполагает следующие опера-

ции:

создание потока;

открытие потока и связывание его с файлом;

обмен (ввод/вывод);

уничтожение потока;

закрытие файла.

Перед тем, как открыть файл для вывода, необходимо объявить объ-

ект типа ofstream:

ofstream outfile (“ name-of-file”);

Проверить, удалось ли нам открыть файл можно следующим образом:

 

if (! outfile )

 

//false, если файл не открыт

 

 

 

 

cerr << “Ошибка открытия файла.\n”

 

 

 

Так же открывается файл и для ввода, только он имеет тип ifstream:

 

ifstream infile (“ name-of-file”);

 

 

 

 

if (! infile)

 

//false, если файл не открыт

 

 

 

 

cerr << “Ошибка открытия файла.\n”

 

 

 

Полный формат команд открытия файла имеет вид:

 

 

 

 

ifstream (const char *name, int mode – ios::in);

//:: - доступ к ios

 

ofstream(const char *name, int mode – ios::out | ios::trunc);

 

Вторым параметром является режим открытия файла:

 

 

 

 

in = 0x01

 

//Открыть для чтения

 

 

 

 

out = 0x02

 

//Открыть для записи

 

 

 

 

ate = 0x04

 

//Установить указатель на конец файла

 

app = 0x08

 

//Открыть для добавления в конец

 

trunk = 0x10

 

//Если файл существует, удалить

 

nocreate = 0x20

 

//Если файл не существует, выдать ошибку

 

noreplace =0x40

 

//Если файл существует, выдать ошибку

 

binary = 0x80

 

//Открыть в двоичном режиме

Эквивалент

Комбинация флагов ios

 

 

 

 

 

 

 

binary

 

in

 

out

 

trunc

 

app

 

stdio

 

 

 

 

+

 

 

 

 

 

 

“w”

 

 

 

 

+

 

 

 

+

 

 

“a”

 

 

+

 

+

 

 

+

 

 

 

“w”

 

 

 

 

 

 

 

 

 

 

“r”

 

 

+

 

+

 

 

 

 

 

 

“r+”

 

 

+

 

+

 

 

+

 

 

 

“w+”

+

 

 

 

+

 

 

 

 

 

 

“wb”

+

 

 

 

+

 

 

 

+

 

 

“ab”

+

 

 

 

+

 

 

+

 

 

 

“wb”

+

 

+

 

 

 

 

 

 

 

 

“rb”

+

 

+

 

+

 

 

 

 

 

 

“r+b”

23

Одесский колледж компьютерных технологий “СЕРВЕР”

+

+

+

+

 

“w+b”

Пример. Написать программу, которая определяет, встречается ли в заданном текстовом файле заданная последовательность символов. Длина строки текста не превішает 80 символов, текст не содержит переносов слов, последовательность не содержит пробельніх символов.

Исходные данные и результаты

1.Текстовый файл неизвестного размера, состоящий из строк длиной не более 80 символов. Поскольку переносы отсутствуют, можно ограничиться поиском заданной последовательности в каждой строке отдельно. Следовательно, необходимо помнить только одну текущую строку файла. Для её хранения выделим строковую переменную длиной 81 символ (дополнительный символ требуется для завершающего нуля).

2.Последовательность символов для поиска, вводимая с клавиатуры. Поскольку по условию задачи она не содержит пробельных символов, её длина тоже не долджна превышать 80 символов, иначе поиск завершится неудачей. Для её хранения также выделим строковую переменную длиной 81 символ.

Результатом работы программы является сообщение либо о наличии заданной последовательности, либо об её отсутствии. Представим варианты сообщений в программе в виде строковых констант.

Для хранения длины строки будем использовать именованную константу. Для работы с файлом потребуется служебная переменная соответствующего типа.

Алгоритм решения задачи:

1.Построчно считывать текст из файла.

2.Для каждой строки проверять, содержится ли в ней заданная последовательность.

3.Если да, то напечатать сообщение о наличии заданной последовательности и завершить программу.

4.При нормальном выходе из цикла напечатать сообщение об отсутствии заданной последовательности и завершить программу.

#include <fstream.h>

 

#include <string.h>

 

int main (){

 

const int len = 81;

//1

char word[len], line[len];

//2

cout << “Введите слово для поиска: ”; cin >> word;

 

ifstream fin(“text.txt”, ios::in | ios::nocreate);

//3

if (!fin) {cout << “Ошибка открытия файла.” << endl;

 

24

Одесский колледж компьютерных технологий “СЕРВЕР”

return 1};

//4

while (fin.getline(line, len)){

//5

if (strstr(line, word)){

//6

cout <<”Присутствует!”<<endl; return 0;}

 

}

 

cout << ” Отсутствует!” << endl;

 

return 0;

//7

}

Воператоре 1 описывается константа, определяющая длину строки файла и длина последовательности. В операторе 2 описывается переменная line для размещения очередной строки файла и переменная word для размещения искомой последовательности символов.

Воператоре 3 определяется объект fin класса входных потоков ifstream. С этим объектом можно работать точно так же, как со стандартными объектами cin и cout, то есть использовать операции помещения в поток << и извлечения из потока >>, а также функции get, getline и другие. Предполагается, что файл с именем text.txt находится в том же каталоге, что и текст программы, иначе он будет иметь специальное значение, например:

ifstream fin(“c:\\prim\\cpp\\text.txt”, ios::in | ios::nocreate); //3

В операторе 4 проверяется успешность создания объекта fin. Файлы, открываемые для чтения, проверять нужно обязательно! В операторе 5 организуется цикл чтения из файла в переменную line. Метод getline, описанный выше, при достижении конца файла вернёт значение, завершающее цикл.

Для анализа строки в операторе 6 применяется функция strstr(line, word). Она выполняет поиск подстроки word в строке line. Обе строки должны завершаться нуль-символами. В случае успешного поиска функция возвращает указатель на найденную подстроку, в случае неудачи – NULL. Если вторым параметром передаётся указатель на строку нулевой длины, функция возвращает указатель на начало строки line.

Пример. Написать программу, которая определяет, сколько раз встретилось заданное слово в текстовом файле, длина строки не превышает 80 символов. Текст не содержит переносов слов.

Отличие от предыдущей задачи: нудно найти не просто последовательность символов, а законченное слово. Слово:

Слово = {начало строки | знак пунктуации | разделитель} символы, составляющие слово {конец строки | знак пунктуации | разделитель} Последовательность решения задачи:

1.Ввести «скелет» программы (директивы #include, функцию main(), описание переменных, открытие файла).

25

Одесский колледж компьютерных технологий “СЕРВЕР”

2.Добавить в программу цикл чтения из файла, внутри цикла поставить контрольный вывод считанной строки.

3.Добавить в программу цикл поиска последовательности символов, составляющих слово, с контрольным выводом.

4.Добавить в программу анализ принадлежности символов, находящихся перед словом и после него, множеству знаков пунктуации и разделителей.

#include <fstream.h> #include <string.h> #include <ctype.h> int main(){

const int len = 81;

char word [len], line [len];

cout << “введите слово для поиска: ”; cin >> word; int l_word = strlen(word);

ifstream fin(“text.txt”, ios::in | ios::nocreate);

if (!fin) {“ошибка открытия файла.” << endl; return 1;}

int count = 0;

while (fin.getline(line, len)){ char *p = line;

while (p = strstr(p,word)){ char *c = p;

p += l_word;

//слово не в начале строки? if (c != line)

//символ перед словом не разделитель?

if (!ispunct (*(c-1)) && !isspace (*(c-1))) continue; //символ после слова разделитель?

if (ispunct(*p) || isspace(*p) || (*p == ‘\0’)) count++;

}

}

cout << “Количество вхождений слова: ” << count << endl; return 0;

}

Вводится служебная переменная c для хранения адреса начала вхождения подстроки. Символы, ограничивающие слово, проверяются с помощью функций ispunct и isspace, которые описаны в заголовочном файле <ctype.h>. Символ, стоящий после слова, проверяется также на признак конца строки.

26

Одесский колледж компьютерных технологий “СЕРВЕР”

Пример. Написать программу, которая считывает текст из файла и выводит на экран только вопросительные предложения из этого текста.

Исходные данные: текстовый файл неизвестного размера, состоящий из неизвестного количества предложений. Предложение может занимать несколько строк, поэтому ограничиться буфером на одну строку в данной задаче нельзя. Выделим буфер, в который поместится весь файл.

Будем хранить длину файла в переменной длинного целого типа. Для организации вывода предложений понадобятся переменные того же типа, хранящие позиции начала и конца предложения.

Алгоритм решения задачи

1.Открыть файл.

2.Определить длину его в байтах.

3.Выделить в динамической памяти буфер соответствующего размера.

4.Считать файл с диска в буфер.

5.Активизируя буфер посимвольно, выделять предложения. Если предложение оканчивается вопросительным знаком, вывести его на экран. Предложение может оканчиваться точкой, восклицательным или вопросительным знаком. В первых двух случаях предложение пропускается. Это выражается в том, что значение позиции начала предложения обновляется. Оно устанавливается равным символу, следующему за текущим, и просмотр продолжается. В случае обнаружения вопросительного знака предложение выводится на экран, после чего также устанавливается новое значение позиции начала предложения.

#include <fstream.h> #include <stdio.h> int main () {

ifstream fin (“text.txt”, ios::in | ios::nocreate);

if (!fin) {cout << “Ошибка открытия файла.” << endl; return 1;}

fin.seekg(0, ios::end);

//1

long len = fin.tellg();

//2

char *buf = new char [len+1];

//3

fin.seekg(0, ios::beg);

//4

fin.read(buf, len);

//5

buf[len] = ‘\0’;

 

long n = 0, i = 0, j=0;

//6

while (buf[i]){

//7

if (buf[i] == ‘?’){

//8

for (j = n; j <= i; j++) cout << buf[j];

 

n = i+1;

 

}

 

if (buf[i] == ‘.’ || buf[i] == ‘!’) n = i+1;

 

27

Одесский колледж компьютерных технологий “СЕРВЕР”

i++;

}

fin.close(); //9 cout << endl;

return 0;

}

Для определения длины файла используются методы seekg и tellg. С любым файлом при его открытии связывается так называемая текущая позиция чтения или записи. Когда файл открывается для чтения, эта позиция устанавливается на начало файла. Для определения длины файла мы перемещаем её на конец файла с помощью метода seekg (оператор 1), а затем с помощью tellg получаем её значение, запомнив его в переменной len (оператор 2).

Метод seekg (offset, org) перемещает текущую позицию чтения из файла на offset байтов относительно org. Параметр org может принимать одно из трёх значений:

ios::beg – от начала файла; ios::cur – от текущей позиции; ios::end – от конца файла.

beg, cur и end являются константами, определёнными в классе ios.

Воператоре 3 выделяется len + 1 байтов под символьную строку buf,

вкоторой будет храниться текст из файла. Мы выделяем на 1 байт больше, чем длина файла, чтобы после считывания файла записать в этот байт нульсимвол.

Для чтения информации требуется снова переместить текущую позицию на начало файла (оператор 4). Собственно чтение выполняется в операторе 5 с помощью метода read(buf, len), который считывает из файла len символов (или менее, если конец файла встретится раньше) в символьный массив buf.

Воператоре 6 определяются служебные переменные. В переменной n будет храниться позиция начала текущего предложения, переменная i используется для просмотра массива, переменная j – для вывода предложения.

Цикл просмотра массива buf (оператор 7) завершается, когда встретился нуль-символ. Если очередным символом оказался вопросительный знак (оператор 8), выполняется вывод символов, начиная с позиции n до текущей, после чего в переменную n заносится позиция начала нового предложения.

Оператор 9 (закрытие потока) в данном случае не является обязательным, так как явный вызов close() необходим только тогда, когда требуется закрыть поток раньше окончания действия его области видимости.

Если требуется вывести результаты выполнения программы не на экран, а в файл, в программе следует описать объект класса выходных потоков ofstream, а затем использовать его аналогично другим потоковым объектам, например:

28