КИТ и Э КБГУ Вторник, 07.05.2024, 05:36
Приветствую Вас Гость | RSS
Меню сайта

Наш опрос
Оцените мой сайт
Всего ответов: 118

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Тема: Работа с файлами в Си. Доступ к файлам.

1.Работа с текстовыми файлами

2.Работа с двоичными файлами

3.Чтение строк из файлов

Практическая работа. Ввод-вывод данных с использованием файлов

Работа с текстовыми файлами

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

Как работать с файлами из программы

Понятие «открыть файл» означает «начать с ним работу», сделать его активным и заблокировать обращение других программ к этому файлу. При закрытии файла он освобождается (теперь с ним могут работать другие программы) и все ваши изменения вносятся на диск.

Для работы с файлом используется специальная переменная, которая называется указателем на файл. Это адрес блока данных в памяти, в котором хранится вся информация об открытом файле. Объявляется указатель на файл так:

FILE *fp;

Чтобы открыть файл, надо вызвать функцию fopen, которая попытается открыть файл и записать его адрес в переменную fp. После этого все обращения к файлу выполняются не по имени файла, а через указатель fp.

fp = fopen ( "qq.dat", "r" );

Здесь файл qq.dat из текущего каталога открывается для чтения (режим "r" во втором параметре функции fopen). Если надо, можно указать полный (или относительный) путь к файлу, например так:

fp = fopen ( "c:\\data\\qq.dat", "r" );

Знак «наклонные черта» (слэш) в символьных строках всегда удваивается, потому что одиночный слэш – это специальный символ, например в сочетании \n.

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

"r" Запись в новый файл. Если на диске уже есть файл с таким именем, он будет

предварительно удален.

"a" Добавление в конец файла. Если на диске уже есть файл с таким именем, новые данные дописываются в конец файла. Если такого файла нет, то он будет создан.

"r+" Открыть существующий файл для изменения с возможностью записи и чтения

"w+" Создание нового файла для записи и чтения (если файл с таким именем уже есть, он заменяется новым).

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

• неверно задано имя файла или файла нет на диске;

• файл используется другой программой и заблокирован.

Если файл открывается на запись, операция может закончиться неудачно, если

• на диске нет места;

• файл защищен от записи;

• неверно задано имя файла (например, оно содержит две точки, знак вопроса и т.п.).

Если файл не удалось открыть, функция fopen возвращает специальное нулевое значение (нулевой указатель), который обозначается NULL. Поэтому надо всегда проверять правильность открытия файла, особенно в режиме чтения. Если файл не был открыт, надо вывести сообщение об ошибке и выйти из программы.

if ( fp == NULL )

{

printf("Нет файла с данными");

return 1; // выход по ошибке, код ошибки 1

}

Если файл открыт, можно читать из него данные. Для того используем функцию fscanf. Она полностью аналогична scanf, но служит для ввода из файла, а не с клавиатуры. Кроме того, ее первый параметр – указатель на файл, из которого читаются данные.

n = fscanf ( fp, "%d", &A[i] );

Функция fscanf возвращает результат – количество чисел, которые ей удалось прочитать.

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

Если файл открыт на запись, можно записать в него данные с помощью функции fprintf, которая полностью аналогична printf.

Когда работа с файлом закончена, надо закрыть его, вызвав функцию fclose:

fclose ( fp );

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

Пример1. Ввести массив из 10 целых чисел из файла input.dat, умножить каждый элемент на 2 и вывести в столбик в файл output.dat.

В программе обрабатываются две ошибки:

• файла нет (его не удалось открыть);

• в файле мало данных или данные неверные (например, слова вместо целых чисел).

#include <stdio.h>

const int N = 10;

main()

{int i, A[N];

FILE *fp; // указатель на файл

fp = fopen( "input.dat", "r" );                                      // открыть файл на чтение

if ( fp == NULL )

{                                                                         // обработка ошибки

printf("Нет файла данных");

return 1;                                                              // выход по ошибке, код ошибки 1

}

for ( i = 0; i < N; i ++ )

if ( 0 == fscanf(fp,"%d",&A[i]) )

{         printf("Не хватает данных в файле");               // чтение и обработка ошибки

break; }

fclose ( fp );                                                        // закрыть файл

for ( i = 0; i < N; i ++ )

A[i] = A[i] * 2;

fp = fopen( "output.dat", "w" );                                     // открыть файл на запись

for ( i = 0; i < N; i ++ )                                         // вывести массив в файл

fprintf ( fp, "%d\n", A[i] );                                   // в столбик

fclose ( fp ); }

В отличие от предыдущих, эта программа выдает результаты не на экран, а в файл

output.dat в текущем каталоге.

 

Пример 2. В файле input.dat записаны в два столбика пары чисел (x,y). Записать в файл output.dat в столбик суммы x+y для каждой пары.

Сложность этой задачи состоит в том, что мы не можем прочитать все данные сразу в память, обработать их и записать в выходной файл. Не можем потому, что не знаем, сколько пар чисел в массиве. Конечно, если известно, что в файле, скажем, не более 200 чисел, можно выделить массив «с запасом», прочитать столько данных, сколько нужно, и работать только с ними. Однако в файле могут быть миллионы чисел и такие массивы не поместятся в памяти.

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

1) открыть два файла, один на чтение (с исходными данными), второй – на запись;

2) попытаться прочитать два числа в переменные x и y; если это не получилось (нет больше данных или неверные данные), закончить работу;

3) сложить x и y и записать результат в выходной файл;

4) перейти к шагу 2.

Для того, чтобы определить, удачно ли закончилось чтение, мы будем использовать тот факт, что функция fscanf (как и scanf) возвращает количество удачно считанных чисел. За один раз будем читать сразу два числа, x и y. Если все закончилось удачно, функция fscanf возвращает значение 2 (обе переменных прочитаны). Если результат этой функции меньше двух, данные закончились или неверные.

Заметим, что надо работать одновременно с двумя открытыми файлами, поэтому в памяти надо использовать два указателя на файлы, они обозначены именами fin и fout. Для сокращения записи ошибки при открытии файлов не обрабатываются.

#include <stdio.h>

main()

{

int n, x, y, sum;

FILE *fin, *fout;                                                            // указатели на файлы

fin = fopen( "input.dat", "r" );                                       // открыть файл на чтение

fout = fopen( "output.dat", "w" );                                  // открыть файл на запись

while ( 1 ) {

n = fscanf ( fin, "%d%d", &x, &y );

if ( n < 2 ) break;                                // данные ошибочны или нет больше данных

sum = x + y;

fprintf ( fout, "%d\n", sum );

}

fclose ( fout );                                                                 // закрыть файлы

fclose ( fin );

}

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

 

Работа с двоичными файлами

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

При открытии двоичного файла вместо режимов "r", "w" и "a" используют соответственно "rb", "wb" и "ab". Дополнительная буква "b" указывает на то, что файл двоичный (от английского слова binary двоичный). Приведем решение одной задачи, которую мы уже разбирали ранее.

Пример 1. Ввести массив из 10 целых чисел из двоичного файла input.dat, умножить каждый элемент на 2 и вывести в двоичный файл output.dat.

#include <stdio.h>

const int N = 10;

main()

{

int i, n, A[N];

FILE *fp;                                                                                // указатель на файл

fp = fopen( "input.dat", "rb" );                             // открыть двоичный файл на чтение

n = fread ( A, sizeof(int), N, fp );                                             // читаем весь массив

if ( n < N ) {                                                                            // обработка ошибки

printf("Не хватает данных в файле");

break;

}

fclose ( fp );                                                                             // закрыть файл

for ( i = 0; i < N; i ++ )

A[i] = A[i] * 2;

fp = fopen( "output.dat", "wb" );                // открыть двоичный файл на запись

fwrite ( A, sizeof(int), N, fp );                     // записать весь массив

fclose ( fp );                                                 // закрыть файл

}

Для чтения из двоичного файла используется функция fread, которая принимает 4 параметра:

адрес области в памяти, куда записать прочитанные данные (в данном случае это адрес первого элемента массива A, который обозначается как &A[0] или просто A);

размер одного элемента данных (лучше сделать так, чтобы машина сама определила его, например, в нашем случае – sizeof(int) – размер целого числа. Хотя в C++ целое число занимает 4 байта, в в других системах программирования это может быть не так; наша программа будет работать и в этом случае, то есть станет переносимой на другую платформу;

количество элементов данных в массиве (N);

указатель на открытый файл, откуда читать данные (fp).

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

Для записи массива в двоичный файл используется функция fwrite с такими же параметрами; она возвращает количество успешно записанных элементов.

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

 

Чтение строк из файлов

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

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

Пример 1. Чтения слова из открытого файла с указателем fp.

#include <stdio.h>

main()

{

char s[80];

FILE *fp;

fp = fopen ( "input.dat", "r" );

fscanf ( fp, "%s", s );

printf ( "Первое слово файла - %s", s );

fclose ( fp );

}

Если надо читать всю строку с пробелами, используют функцию fgets. Она принимает три параметра:

имя символьной строки, в которую записать данные;

максимальную длину этой строки; функция не допускает выхода за границы строки; если строка в файле длиннее, чем можно записать в память, читается только начальная часть, а остальное – при следующих вызовах fgets;

указатель на файл.

Если функция fgets не может прочитать строку из файла (например, если нет больше строк), то она возвращает в качестве результата специальное значение NULL. Это свойство можно использовать для обработки ошибок и прекращения ввода данных.

 

#include <stdio.h>

main()

{

char s[80];

FILE *fp;

fp = fopen ( "input.dat", "r" );

if ( NULL == fgets ( s, 80, fp ) )

printf ( "Не удалось прочитать строку" );

else

printf ( "Первая строка файла - %s", s );

fclose ( fp );}

Функция fgets читает строку из файла, пока не случится одно из двух событий:

• встретится символ перехода на новую строку '\n';

• прочитано столько символов, что они заняли всю строку (с учетом последнего нуля), например, в нашем случае она остановится, если прочтет 79 символов.

В конце строки будет поставлен символ '\0'. Кроме того, если был найден символ перехода на новую строку '\n', он сохраняется и в строке s.

 

Пример 2. В каждой строке файла input.dat заменить все буквы 'A' на 'Б' и вывести измененный текст в файл output.dat.

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

Для чтения из файла используем цикл while. Он заканчивает работу, если функция fgets вернет значение NULL, то есть все строки обработаны.

#include <stdio.h>

main()

{

char s[80];

int i;

FILE *fin, *fout;

fin = fopen ( "input.dat", "r" );

fout = fopen ( "output.dat", "w" );

while ( NULL != fgets ( s, 80, fin ) )                     // читаем строку

{ i = 0; // начинаем с s[0]

while ( s[i] != '\0' ) {                                             // пока не конец строки

if ( s[i] == 'А' ) s[i] = 'Б';                                                // меняем символ

i ++;                                         // переходим к следующему символу

}

fprintf ( fout, "%s", s );                               // выводим строку в файл

}

fclose ( fin );

fclose ( fout );}

Обратите внимание, что мы не поставили символ перехода на новую строку при вызове функции fprintf. Это связано с тем, что при чтении функция fgets сохраняет символ '\n' в конце каждой строки (кроме последней), поэтому строки будут выведены в выходной файл так же, как они были записаны в исходном файле.

Вход на сайт

Поиск

Календарь
«  Май 2024  »
ПнВтСрЧтПтСбВс
  12345
6789101112
13141516171819
20212223242526
2728293031

Архив записей

Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz

  • Copyright Fatima_Zh © 2024Бесплатный хостинг uCoz