Функция fputc
#include <stdio.h>
int fputc(int ch, FILE *stream);
Функция fputc() записывает символ ch в текущую позицию потока stream, a затем увеличивает указатель текущей позиции файла. Хотя на практике при объявлении символа ch он всегда имеет тип int, функцией fputc() тип символа преобразуется в unsigned char. Поскольку в момент вызова символьный аргумент преобразуется к целому типу, в качестве аргументов обычно можно использовать и символьные переменные. При использовании целого значения, старший байт попросту отбрасывается.
Значением, возвращаемым функцией fputc(), является значение записанного символа. При возникновении ошибки возвращается значение EOF. Если файл открыт для выполнения операций в двоичном режиме, значение EOF тоже может оказаться символом. Поэтому, чтобы определить, возникла ли ошибка на самом деле, в таких случаях придется использовать функцию ferror().
Пример
Приведенная функция записывает в заданный поток содержимое строки.
void write_string(char *str, FILE *fp)
{
while(*str) if(!ferror(fp)) fputc(*str++, fp);
}
Функция fgetc
#include <stdio.h>
int fgetc(FILE *stream);
Функция fgetc() возвращает символ, взятый из входного потока stream и находящийся сразу после текущей позиции, а также увеличивает указатель текущей позиции файла. Этот символ читается как значение типа unsigned char, преобразованное в целое.
При достижении конца файла функция fgetc() возвращает значение EOF. Ho по скольку значение EOF является действительным целым значением, при работе с двоичными файлами для обнаружения конца файла необходимо использовать функцию feof(). Если функция fgetc() обнаруживает ошибку, она возвращает значение EOF. Для выявления ошибок, возникающих при работе с двоичными файлами, необходимо использовать функцию ferror().
Пример
Следующая программа читает и выводит на экран содержимое текстового файла.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
char ch;
if((fp=fopen(argv[1],"r"))==NULL) {
printf("Невозможно открыть файл.\n");
exit(1);
}
while((ch=fgetc(fp)) != EOF) {
printf("%c", ch);
}
fclose(fp);
return 0;
}
Функция fprintf
#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);
Функция fprintf() выводит в поток, адресуемый параметром stream, значения аргументов, составляющих список аргументов, в соответствии с заданной строкой формата format. Возвращаемое значение равно количеству реально выведенных символов. Если при выводе возникла ошибка, возвращается отрицательное число.
#include <stdio.h>
int fscanf(FILE *stream, const char *format, ...);
Функция fscanf() работает подобно функции scanf(), но читает информацию не из стандартного потока ввода stdin, а из потока, заданного указателем stream. Подробности рассматриваются в разделе этой главы, посвященном функции scanf.
Функция fscanf() возвращает количество аргументов, которым действительно присвоены значения. Это число не включает опущенные поля. Если возвращаемое функцией значение равно EOF, то это свидетельствует о том, что до выполнения первого присваивания произошел сбой.
#include <stdio.h>
main( )
{
FILE *fi;
int age;
fi=fopen("File","r"); /* считывание */
fscanf(fi,"%d",&age); /* fi указывает на File */
fclose(fi);
fi=fopen("Data", "a"); /*дополнение*/
fprintf(fi,"Data is %d.\n",age);
/*fi указывает на Data*/
fclose(fi);
}
Функция fgets( )
include <stdio.h>
char *fgets(char *str, int num, FILE *stream);
Функция fgets() читает из входного потока stream не более num-1 символов и помещает их в массив символов, адресуемый указателем str. Символы читаются до тех пор, пока не будет прочитан символ новой строки или значение EOF, либо пока не будет достигнут заданный предел. По завершении чтения символов сразу же за последним из них размещается нулевой символ. Символ новой строки сохраняется и становится частью массива, адресуемого элементом str.
/* Программа считывает файл строка за строкой */
#include <stdio.h>
#define MAX 80
main( )
{
FILE *f1;
char *string[MAX];
f1=fopen("File","r");
while (fgets(string,MAX,f1) != NULL)
puts(string);
}
Мы расположили вводимую информацию в символьном массиве string. Первый из трех аргументов функции fgets( ) является указателем на местоположение считываемой строки. Второй аргумент содержит предельную длину считываемой строки. Функция прекращает работу после считывания символа новой строки или после считывания символов общим числом MAX-1, в зависимости от того, что произойдет раньше. В любом случае нуль-символ '\0' добавляется в самый конец строки. Третий аргумент указывает на файл, который будет читаться.
Разница между gets( ) и fgets( ) заключается в том, что gets( ) заменяет символ новой строки на '\0', в то время как fgets( ) сохраняет символ новой строки. Подобно gets( ) функция fgets( ) возвращает значение NULL, если встречает символ EOF . Это позволяет нам проверить, достигли ли мы конца файла.
Функция fputs( )
#include <stdio.h>
int fputs(const char *str, FILE *stream);
Функция fputs() записывает в заданный поток stream содержимое строки, адресуемой указателем str. При этом завершающий нулевой символ (т.е. символ конца строки ('0')) не записывается.
В версии C99 к параметрам str и stream применен квалификатор restrict.
При успешном выполнении функция fputs() возвращает неотрицательное значение, а при неудачном — значение EOF.
Если поток открыт в текстовом режиме, могут произойти преобразования некоторых символов. Это значит, что однозначного отображения строки в файл может и не быть. Однако если поток открыт в двоичном режиме, никаких преобразований символов не будет и строка отобразится в файл "один к одному".
l=fputs("Строка", fi);
|
|
|
|
|
|
|
|
Передает строку "Строка" в файл, на который ссылается указатель fi типа FILE. Конечно, сначала нужно открыть файл при помощи функции fopen( ).
|
Поток по умолчанию буферизован, поэтому закрытие файла освобождает буфер. Однако имеется возможность явно указать освобождение буфера в любой момент работы программы, не закрывая файл. Для этого используется функция
int fflush(FILE *имя); |
Аналогично функции закрытия файла в случае успешной записи буфера в файл возвращается ноль, иначе — EOF. Важнейшей функцией является функция, проверяющая, достигнут ли конец файла. Ее прототип
int feof(FILE *имя); |
Очевидно, конец файла может обнаружиться только при чтении из файла. Если в результате очередной операции ввода достигнут конец файла, система устанавливает внутренний индикатор конца файла. Этот индикатор и проверяет данная функция. Если достигнут конец файла, то функция возвращает ненулевое значение, иначе возвращается 0. Двоичные файлы Двоичные и текстовые файлы — это, как говорят в Одессе, «две большие разницы». Обмен данными между программой и двоичным потоком выполняется без всякого преобразования, поэтому работает быстрее. Двоичный ввод выполняется функцией fread(), имеющей следующий прототип:
size_t fread(void *buffer, size_t size, size_t n, FILE *stream); |
Тип size_t обычно определен как unsigned int. Первый параметр часто определяет массив (или указатель на динамический массив), в который будет прочитана информация; третий параметр задает размер одного элемента данных в байтах, а второй — количество читаемых элементов. Четвертый параметр определяет двоичный файл, из которого информация вводится. Общее количество считанных байтов равно size*n. Однако возвращает функция количество корректно прочитанных элементов, а не байтов. Вывод в двоичный файл выполняется функцией fwrite(), которая имеет совершенно аналогичный прототип:
size_t fwrite(const void *buffer, size_t size, size_t n, FILE *stream); |
Функция записывает n элементов размера size в двоичный файл stream из буфера, указатель на который задается в качестве первого аргумента. Общее количество выводимых байтов равно size*n. Однако возвращает функция количество корректно записанных элементов, а не байтов. В качестве элементов могут использоваться любые переменные любых типов, в том числе и динамические. Даже массив может быть одним-единственным элементом! Рассмотрим несколько простых примеров, аналогичных примерам для текстовых файлов (см. листинги 10.8,10.9). Создадим на диске C: каталог BinFiles и все двоичные файлы будем размещать в нем. Переделаем пример создания файла (листинг 10.12).
//Листинг 10.12. Создание и чтение двоичных файлов #include <cstdio> #include <cstdlib> #include <ctime> int main() { int m[10]={0}; srand((unsigned)time(NULL)); // инициализация датчика случайных чисел FILE *stream; /* открываем двоичный файл для записи */ if((stream = fopen("c:/binfiles/number1.bin", "wb" )) == NULL) return 1; // ошибка при открытии /* заполняем массив m числами */ for(int i = 0; i < 10; i++) m[i] = rand()%10; // случайные числа от 0 до 9 // заполняем файл number1.bin элементами-числами for(int i = 0; i < 10; i++) fwrite(&m[i], 1, sizeof(int), stream); fclose(stream); // закрываем файл /* открываем другой файл для записи */ if((stream = fopen("c:/binfiles/number2.bin", "wb" )) == NULL) return 1; // ошибка при открытии // заполняем файл number2.bin элементом-массивом fwrite(m, 1, sizeof(m), stream); // массив - один элемент fclose(stream); // закрываем файл // вывод второго двоичного файла на экран // открываем файл для чтения if((stream = fopen("c:/binfiles/number2.bin", "rb" )) == NULL) return 1; // ошибка при открытии int a = 0; // сюда вводим // читаем второй файл поэлементно // правильный цикл fread(&a, 1, sizeof(int), stream); // предварительное чтение while(!feof(stream)) // пока не конец файла { printf("%d\n", a); fread(&a, 1, sizeof(int), stream); } fclose(stream); // открываем первый файл в режиме чтения if((stream = fopen("c:/binfiles/number1.bin", "rb" )) == NULL) return 1; int t[10] = {0}; // массив для чтения // читаем первый файл как массив fread(t, 1, sizeof(t), stream); for(int i = 0; i < 10; i++) // выводим поэлементно printf("%d\n",t[i]); fclose(stream); char ch = getchar(); return 0; } |
В этой программе сначала создается два двоичных файла: number1.bin и number2.bin. В первый файл целые числа из массива m записываются по одному в цикле. Во второй файл весь массив записывается сразу как один элемент. Затем файлы открываются для чтения. Сначала открывается файл number2.bin (в который мы писали массив целиком), и чтение из него выполняется по одному числу. Проверка конца файла делается так же, как и для текстовых файлов. На экране видно, что чтение выполняется совершенно правильно. Первый файл number1.bin, который записывался в цикле по одному числу, читается сразу целиком в массив t, а вывод осуществляется по одному числу. И снова мы наблюдаем на экране, что чтение выполнилось совершенно правильно. Такое «смешивание» для двоичных файлов безопасно, так как и в памяти, и на диске размеры данных равны sizeof(тип)*n, где n — количество элементов, участвующих в обмене. Теперь добавим в конец этого примера строки, выполняющие суммирование чисел, записанных в файл number1.bin.
// суммирование чисел записанных в файле if((stream = fopen("c:/binfiles/number1.bin", "rb" )) == NULL) return 1; // ошибка при открытии // читаем числа по одному из файла и считаем int number, summa = 0, count = 0; fread(&number, 1, sizeof(int), stream); while(!feof(stream)) { printf("%d\n", number); ++count; summa+=number; fread(&number, 1, sizeof(int), stream); } printf("%d %d\n", summa, count); fclose(stream); |
Как видим, этот текст отличается от приведенного выше для текстовых файлов только режимом открытия и функцией ввода данных. Так как функции посимвольного ввода/вывода fgetc() и fputc() фактически не выполняют никакого преобразования информации, то копирование двоичных файлов можно делать точно так же, как и текстовых — посимвольно. Используем функцию filecopy(), представленную в листинге 10.9.
// копирование файлов FILE *in, *out; if((in = fopen("c:/binfiles/number1.bin", "rb" )) == NULL) { printf ("Error input file!"); return 1; // ошибка при открытии } if((out = fopen("c:/binfiles/number1.new", "wb" )) == NULL) { printf ("Error output file!"); return 1; // ошибка при открытии } filecopy(in, out); // копирование fclose(in); fclose(out); |
Соответственно и для объединения файлов можно использовать ту же функцию — все определяется режимом открытия файла.
// дозапись нового файла в конец старого if((out = fopen("c:/binfiles/number1.bin", "ab" )) == NULL) { printf ("Error output file!"); return 1; } if((in = fopen("c:/binfiles/number1.new", "rb" )) == NULL) { printf ("Error input file!"); return 1; } filecopy(in, out); // объединение – дозапись! fclose(in); fclose(out); |
Для проверки правильности объединения выведем содержимое файла на экран. Используем вторую форму проверки окончания файла — внутри цикла:
// вывод нового файла на экран для проверки if((stream = fopen("c:/binfiles/number1.bin", "rb" )) == NULL) return 1; while(true) { fread(&a, 1, sizeof(int), stream); if (feof(stream)) break; printf("%d\n", a); } fclose(stream); |
Пример работы с потоками
Приведенная ниже программа построчно считывает данные из текстового файла в буферную переменную s, формирует из них структуру mon и записывает ее в двоичном режиме в выходной файл. Далее иллюстрируется считывание из этого файла произвольной записи.
#include <cstdio>
#include <cstdlib>
#include <cconio>
#include <iostream>
using namespace std;
int main()
{
FILE *fi, *fo;
if((fi = fopen("d:\\c\\binfile.txt", "r"))==0)
{
cout << "Ошибка открытия входного файла";
return 1;
};
if((fo = fopen("d:\\c\\binfile.out", "w+b"))==0)
{
cout << "Ошибка открытия выходного файла";
return 1;
};
const int dl = 80;
char s[dl];
struct
{
char type[20];
int opt, rozn;
char comm[40];
}mon;
while (fgets(s, dl, fi))
{
// Преобразование строки в структуру:
strncpy(mon.type, s, 19); //Описание strncpy см. в след. разделе
mon.opt = atoi(&s[20]); //Описание atoi см. в след. разделе
mon.rozn = atoi(&s[25]);
strncpy(mon.comm, &s[30], 40);
fwrite(&mon, sizeof mon, 1, fo);
}
fclose(fi);
int i; cin >> i; // Номер записи
// Установка указателя текущей позиции файла на запись 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);
}
Пример Текстовый файл состоит не более чем из ста различных слов.Слово – последовательность латинских букв(A..Z,a..z). Слова разделяются любыми символами. Слова в тексте могут повторятся. Для каждого слова необходимо указать какое количество раз оно встречается в тексте. Маленькие и большие буквы считать наразличимыми.( «Инфоматика»= «информатика») Имена входного и выходного файла задаются как параметры запуска,если при запуске имена не указаны, программа выполняет их ввод имен файлов с клавиатуры.
#include <cstdio>
#include <cstring>
#include <cctype>
//Дан текст состоящий из латинских букв, цифр и разделителей
//Словом назовем последовательность латинских букв. Известно, что в исходном тексте не более
//100 различных слов(без учета регистра).
//Создать файл в котором для каждого слова, встречающегося в исходном файле, укзавается
//Количество повторений
//пример Исходный файл:
// Ура! ура!
// Каникулы - счастливая пора!УРА
// Результат:
//Ура - 3
//Каникулы - 1
//счастливая - 1
//пора - 1
//Имена файлов задаются как параметры командной строки(если не заданы, то вводятся с клавиатуры)
// сравнение строк без учета регистра
int strcmp_ign_case(const char *a,const char *b){
while(*a && *b){
if(tolower(*a)!=tolower(*b)) break;
++a;
++b;
}
return (*a)-(*b);
}
//Построение таблицы слов
int addSlovoToTable(char * arStr[],const char *str, int count[],int &n){
for(int i = 0;i< n ; i++)// Цикл поиска слова в таблице
if (!strcmp_ign_case(arStr[i],str)){
count[i]++; // Если слово уже занесено, увеличиваем счетчик
return 0;
}
arStr[n] = new char[strlen(str)+1];//Заносим новое слово
strcpy(arStr[n],str);
count[n]++;
n++;
return 0;
}
int main(int argc,char *argv[]){
char *arrayStr[100];//Таблица слов
int cnt[100]; // Таблица повторений
FILE *fi,*fo; // Файлы
int i =0;
char nameFileInput[100];
char nameFileOutput[100];
char str[100];
char *delemited = ",.-!?0123456789_ ;";
for(i =0;i<100;i++)cnt[i] = 0;
// Открытие файлов с проверкой наличия параметров коммандной строки
if (argc<2){
printf("ne zadano imy vhodnogo faila pri zapuske\n vvedite :");
gets(nameFileInput);
printf("ne zadano imy vyhodnogo faila pri zapuske\n vvedite :");
gets(nameFileOutput);
}
else{
strcpy(nameFileInput,argv[1]);
if(argc<3){
printf("ne zadano imy vyhodnogo faila pri zapuske\n vvedite :");
gets(nameFileOutput);
}
else{
strcpy(nameFileOutput,argv[2]);
}
}
if(( fi=fopen(nameFileInput,"r"))==NULL){
printf(" Fail dly chteniy ne naiden");
return 1;
}
if(( fo=fopen(nameFileOutput,"w"))==NULL){
printf(" Fail dly zapici ne naiden");
return 1;
}
i = 0;
// Цикл чтения из файла
while(!feof(fi)){
fgets(str,100,fi);
if(str[strlen(str)-1]=='\n'){str[strlen(str)-1]='\0';}// Убираем символ перевода на новую строку
puts(str);
char *ptr;
ptr = strtok(str,delemited);// Находим первое слово
while(ptr){
puts(ptr);
addSlovoToTable(arrayStr,ptr, cnt,i);// Добавляем слово в таблицу
ptr = strtok(NULL,delemited); // Находим следующее слово
}
}
// Вывод в файл и освобождение памяти
for(int j=0;j<i;j++){
fprintf(fo," %s - %d\n",arrayStr[j],cnt[j]);
delete []arrayStr[j];
}
fclose(fi);
fclose(fo);
return 0;
}
