Перейти к содержимому

Как считать весь файл в строку c

  • автор:

Ввод данных из файла и вывод в файл в языке программирования С

До этого при вводе-выводе данных мы работали со стандартными потоками — клавиатурой и монитором. Теперь рассмотрим, как в Си реализовано получение данных из файлов и запись их туда. Перед тем как выполнять эти операции, надо открыть файл и получить доступ к нему.

В языке программирования C указатель на файл имеет тип FILE и его объявление выглядит так:

FILE *myfile;

С другой стороны, функция fopen() открывает файл по указанному в качестве первого аргумента адресу в режиме чтения «r», записи «w» или добавления «a» и возвращает в программу указатель на него. Поэтому процесс открытия файла и подключения его к программе выглядит примерно так:

myfile = fopen("hello.txt", "r");

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

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

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

Объявление функции fopen содержится в заголовочном файле stdio.h , поэтому требуется его подключение. Также в stdio.h объявлен тип-структура FILE .

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

fclose(myfile);

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

Чтение из текстового файла и запись в него

fscanf

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

fscanf(myfile, "%s%d", str, &a); 

Возвращает количество удачно считанных данных или EOF. Пробелы, символы перехода на новую строку учитываются как разделители данных.

Допустим, у нас есть файл содержащий такое описание объектов:

apples 10 23.4 bananas 5 25.0 bread 1 10.3

Тогда, чтобы считать эти данные, мы можем написать такую программу:

#include int main () { FILE *file; struct food { char name[20]; unsigned qty; float price; }; struct food shop[10]; char i = 0; file = fopen("goods.txt", "r"); while (fscanf(file, "%s%u%f", shop[i].name, &(shop[i].qty), &(shop[i].price)) != EOF) { printf("%s %u %.2f\n", shop[i].name, shop[i].qty, shop[i].price); i++; } }

В данном случае объявляется структура и массив структур. Каждая строка из файла соответствует одному элементу массива; элемент массива представляет собой структуру, содержащую строковое и два числовых поля. За одну итерацию цикл считывает одну строку. Когда встречается конец файла fscanf() возвращает значение EOF и цикл завершается.

fgets

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

fgets(массив_символов, количество_считываемых_символов, указатель_на_файл)
fgets(str, 50, myfile)

Такой вызов функции прочитает из файла, связанного с указателем myfile , одну строку текста полностью, если ее длина меньше 50 символов с учетом символа ‘\n’, который функция также сохранит в массиве. Последним (50-ым) элементом массива str будет символ ‘\0’, добавленный fgets . Если строка окажется длиннее, то функция прочитает 49 символов и в конце запишет ‘\0’. В таком случае ‘\n’ в считанной строке содержаться не будет.

#include #define N 80 int main() { FILE *file; char arr[N]; file = fopen("goods.txt", "r"); while (fgets(arr, N, file) != NULL) printf("%s", arr); printf("\n"); fclose(file); }

В этой программе в отличие от предыдущей данные считываются строка за строкой в массив arr . Когда считывается следующая строка, предыдущая теряется. Функция fgets() возвращает NULL в случае, если не может прочитать следующую строку.

getc или fgetc

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

#include #define N 80 int main() { FILE *file; char str[N]; int i; file = fopen("goods.txt", "r"); while ((str[i] = fgetc(file)) != EOF) { if (str[i] == '\n') { str[i] = '\0'; printf("%s\n", str); i = 0; } else i++; } str[i] = '\0'; printf("%s\n",str); fclose(file); }

Приведенный в качестве примера код выводит данные из файла на экран.

Запись в текстовый файл

Также как и ввод, вывод в файл может быть различным.

  • Форматированный вывод. Функция fprintf( файловый_указатель, строка_формата, переменные ) .
  • Построчный вывод. Функция fputs( строка, файловый_указатель ) .
  • Посимвольный вывод. Функция fputc() или putc( символ, файловый_указатель ) .

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

Запись в каждую строку файла полей одной структуры (прерывание в Linux ‒ Ctrl + D ):

#include #define N 80 int main() { FILE *file; char str[N]; int i; file = fopen("goods.txt", "r"); while ((str[i] = fgetc(file)) != EOF) { if (str[i] == '\n') { str[i] = '\0'; printf("%s\n", str); i = 0; } else i++; } str[i] = '\0'; printf("%s\n",str); fclose(file); }

Построчный вывод в файл ( fputs() , в отличие от puts() сама не помещает в конце строки ‘\n’):

#include int main () { FILE *file; char str[10]; file = fopen("output.txt", "w"); while (scanf("%s", str) != EOF) { fputs(str, file); fputs("\n", file); } fclose(file); }

Пример посимвольного вывода:

#include int main() { FILE *file; char i; file = fopen("output.txt", "w"); while ((i = getchar()) != EOF) fputc(i, file); fclose(file); }

Чтение из двоичного файла и запись в него

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

При открытии файла для двоичного доступа, вторым аргументом fopen() является строка «rb» или «wb».

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

Функции fread() и fwrite() имеют следующие параметры:

  1. адрес области памяти, куда данные записываются или откуда считываются,
  2. размер одного данного какого-либо типа,
  3. количество считываемых данных указанного размера,
  4. файловый указатель.

Эти функции возвращают количество успешно прочитанных или записанных данных. Т.е. можно «заказать» считывание 50 элементов данных, а получить только 10. Ошибки при этом не возникнет.

Пример использования функций fread() и fwrite() :

#include #include int main() { FILE *file; char shelf1[50], shelf2[100]; int n, m; file = fopen("shelf1.txt", "rb"); n = fread(shelf1, sizeof(char), 50, file); fclose(file); file = fopen("shelf2.txt", "rb"); m = fread(shelf2, sizeof(char), 50, file); fclose(file); shelf1[n] = '\0'; shelf2[m] = '\n'; shelf2[m+1] = '\0'; file = fopen("shop.txt", "wb"); fwrite(strcat(shelf2, shelf1), sizeof(char), n+m, file); fclose(file); }

Здесь осуществляется попытка чтения из первого файла 50-ти символов. В n сохраняется количество реально считанных символов. Значение n может быть равно 50 или меньше. Данные помещаются в строку. То же самое происходит со вторым файлом. Далее первая строка присоединяется ко второй, и данные сбрасываются в третий файл.

Курс с решением задач:
pdf-версия

Как считать весь файл в строку c

Потоки для работы с текстовыми файлами представляют объекты, для которых не задан режим открытия ios::binary .

Запись в файл

Для записи в файл к объекту ofstream или fstream применяется оператор
#include #include int main() < std::ofstream out; // поток для записи out.open("hello.txt"); // открываем файл для записи if (out.is_open()) < out out.close(); std::cout

Здесь предполагается, что файла «hello.txt» располагается в одной папке с файлом программы. Данный способ перезаписывает файл заново. Если надо дозаписать текст в конец файла, то для открытия файла нужно использовать режим ios::app :

std::ofstream out("hello.txt", std::ios::app); if (out.is_open()) < out out.close();

Чтение из файла

Если надо считать всю строку целиком или даже все строки из файла, то лучше использовать встроенную функцию getline() , которая принимает поток для чтения и переменную, в которую надо считать текст:

#include #include #include // для std::getline int main() < std::string line; std::ifstream in("hello.txt"); // окрываем файл для чтения if (in.is_open()) < while (std::getline(in, line)) < std::cout > in.close(); // закрываем файл >

Также для чтения данных из файла для объектов ifstream и fstream может применяться оператор >> (также как и при чтении с консоли):

#include #include #include struct Point < Point(double x, double y): x, y <> double x; double y; >; int main() < std::vectorpoints< Point, Point, Point>; std::ofstream out("points.txt"); if (out.is_open()) < // записываем все объекты Point в файл for (const Point& point: points) < out > out.close(); std::vector new_points; std::ifstream in("points.txt"); // окрываем файл для чтения if (in.is_open()) < double x, y; while (in >> x >> y) < new_points.push_back(Point); > > in.close(); for (const Point& point: new_points) < std::cout >

Здесь вектор структур Point записывается в файл.

for (const Point& point: points)

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

0 0 4 5 -5 7

Используя оператор >>, можно считать последовательно данные в переменные x и y и ими инициализировать структуру.

double x, y; while (in >> x >> y) < new_points.push_back(Point); >

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

Как считать весь файл в строку c

При работе с текстовыми файлами граздо проще работать с данными не как с отдельными символами, а как со строками с помощью функций fgets() и fputs() .

Запись текстового файла

Функция fputs() записывает в файл строку, то есть набор символов, который завершается символом '\0'. Она имеет следующий прототип:

int fputs(const char *s, FILE *stream);

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

При записи строки нулевой символ '\0' в файл не записывается.

Например, запишем строку в файл:

#include int main(void) < // строка для записи char * message = "Hello METANIT.COM!\nAn apple a day keeps the doctor away"; // файл для записи char * filename = "data.txt"; // запись в файл FILE *fp = fopen(filename, "w"); if(fp) < // записываем строку fputs(message, fp); fclose(fp); printf("File has been written\n"); >>

Запись довольно проста: открываем файл на запись и с помощью вызова fputs(message, fp) записываем в файл искомую строку.

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

Функция fgets() считывает из файла одну строку и имеет следующий прототип:

char * fgets(char *s, int n, FILE *sream);

Первый параметр char *s представляет строку, в которую производится считывание. Второй параметр - число n указывает, сколько символов необходимо считывать. И третий параметр представляет файловый поток, из которого производится считывание.

При вызове функция считывает из файла не более n-1 символов. Функция прекращает чтение, когда прочтет n-1 символов или встретит символ переноса строки \n. Все считанные символы записываются в строку s, в том числе символ \n. И также конец каждой строки дописывается нулевой символ '\0'.

При успешном завершении функция возвращает указатель s , а при ошибке или достижении конца файла возвращается значение NULL .

Считаем данные из выше записанного файла "data.txt":

#include int main(void) < // файл чтения char * filename = "data.txt"; // буфер для считавания данных из файла char buffer[256]; // чтение из файла FILE *fp = fopen(filename, "r"); if(fp) < // пока не дойдем до конца, считываем по 256 байт while((fgets(buffer, 256, fp))!=NULL) < printf("%s", buffer); >fclose(fp); > >

Здеcь открываем файл на чтение и в цикле считываем из файла по 256 символов и выводим их на консоль с помощью вызова fgets(cc, 256, fp) . Когда данные в файле закончатся, функция возвратит NULL, и произойдет выход из цикла.

Копирование файлов

Другой пример работы с текстовыми файлами - копирование содержимого из одного файла в другой:

#include int main(void) < char * filename1 = "data1.txt"; char * filename2 = "data2.txt"; char buffer[256]; FILE *f1 = fopen(filename1, "r"); // файл на чтение FILE *f2 = fopen(filename2, "w"); // файл на запись if(!f1 || !f2) < printf("Error occured while opening file\n"); >else < // пока не дойдем до конца, считываем по 256 байт из файла f1 while((fgets(buffer, 256, f1))!=NULL) < // записываем строку в файл f2 fputs(buffer, f2); printf("%s", buffer); >> fclose(f1); fclose(f2); return 0; >

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

Теги: Текстовые файлы, fopen, fclose, feof, setbuf, setvbuf, fflush, fgetc, fprintf, fscanf, fgets, буферизированный поток, небуферизированный поток.

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

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

  • 1. Открыть файл, для того, чтобы к нему можно было обращаться. Соответственно, открывать можно для чтения, записи, чтения и записи, переписывания или записи в конец файла и т.п. Когда вы открываете файл, может также произойти куча ошибок – файла может не существовать, это может быть файл не того типа, у вас может не быть прав на работу с файлом и т.д. Всё это необходимо учитывать.
  • 2. Непосредственно работа с файлом - запись и чтение. Здесь также нужно помнить, что мы работаем не с памятью с произвольным доступом, а с буферизированным потоком, что добавляет свою специфику.
  • 3. Закрыть файл. Так как файл является внешним по отношению к программе ресурсом, то если его не закрыть, то он продолжит висеть в памяти, возможно, даже после закрытия программы (например, нельзя будет удалить открытый файл или внести изменения и т.п.). Кроме того, иногда необходимо не закрывать, а "переоткрывать" файл для того, чтобы, например, изменить режим доступа.

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

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

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

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

Создание и выделение памяти под объект типа FILE осуществляется с помощью функции fopen или tmpfile (есть и другие, но мы остановимся только на этих).

Функция fopen открывает файл. Она получает два аргумента – строку с адресом файла и строку с режимом доступа к файлу. Имя файла может быть как абсолютным, так и относительным. fopen возвращает указатель на объект FILE, с помощью которого далее можно осуществлять доступ к файлу.

FILE* fopen(const char* filename, const char* mode);

Например, откроем файл и запишем в него Hello World

#include #include #include void main() < //С помощью переменной file будем осуществлять доступ к файлу FILE *file; //Открываем текстовый файл с правами на запись file = fopen("C:/c/test.txt", "w+t"); //Пишем в файл fprintf(file, "Hello, World!"); //Закрываем файл fclose(file); getch(); >

Функция fopen сама выделяет память под объект, очистка проводится функцией fclose. Закрывать файл обязательно, самостоятельно он не закроется.

Функция fopen может открывать файл в текстовом или бинарном режиме. По умолчанию используется текстовый. Режим доступа может быть следующим

Параметры доступа к файлу.

Тип Описание
r Чтение. Файл должен существовать.
w Запись нового файла. Если файл с таким именем уже существует, то его содержимое будет потеряно.
a Запись в конец файла. Операции позиционирования (fseek, fsetpos, frewind) игнорируются. Файл создаётся, если не существовал.
r+ Чтение и обновление. Можно как читать, так и писать. Файл должен существовать.
w+ Запись и обновление. Создаётся новый файл. Если файл с таким именем уже существует, то его содержимое будет потеряно. Можно как писать, так и читать.
a+ Запись в конец и обновление. Операции позиционирования работают только для чтения, для записи игнорируются. Если файл не существовал, то будет создан новый.

Если необходимо открыть файл в бинарном режиме, то в конец строки добавляется буква b, например “rb”, “wb”, “ab”, или, для смешанного режима “ab+”, “wb+”, “ab+”. Вместо b можно добавлять букву t, тогда файл будет открываться в текстовом режиме. Это зависит от реализации. В новом стандарте си (2011) буква x означает, что функция fopen должна завершиться с ошибкой, если файл уже существует. Дополним нашу старую программу: заново откроем файл и считаем, что мы туда записали.

#include #include #include void main() < FILE *file; char buffer[128]; file = fopen("C:/c/test.txt", "w"); fprintf(file, "Hello, World!"); fclose(file); file = fopen("C:/c/test.txt", "r"); fgets(buffer, 127, file); printf("%s", buffer); fclose(file); getch(); >

Вместо функции fgets можно было использовать fscanf, но нужно помнить, что она может считать строку только до первого пробела.
fscanf(file, "%127s", buffer);

Также, вместо того, чтобы открывать и закрывать файл можно воспользоваться функцией freopen, которая «переоткрывает» файл с новыми правами доступа.

#include #include #include void main() < FILE *file; char buffer[128]; file = fopen("C:/c/test.txt", "w"); fprintf(file, "Hello, World!"); freopen("C:/c/test.txt", "r", file); fgets(buffer, 127, file); printf("%s", buffer); fclose(file); getch(); >

Функции fprintf и fscanf отличаются от printf и scanf только тем, что принимают в качестве первого аргумента указатель на FILE, в который они будут выводить или из которого они будут читать данные. Здесь стоит сразу же добавить, что функции printf и scanf могут быть без проблем заменены функциями fprintf и fscanf. В ОС (мы рассматриваем самые распространённые и адекватные операционные системы) существует три стандартных потока: стандартный поток вывода stdout, стандартный поток ввода stdin и стандартный поток вывода ошибок stderr. Они автоматически открываются во время запуска приложения и связаны с консолью. Пример

#include #include #include void main() < int a, b; fprintf(stdout, "Enter two numbers\n"); fscanf(stdin, "%d", &a); fscanf(stdin, "%d", &b); if (b == 0) < fprintf(stderr, "Error: divide by zero"); >else < fprintf(stdout, "%.3f", (float) a / (float) b); >getch(); >

Ошибка открытия файла

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

#include #include #include #define ERROR_OPEN_FILE -3 void main() < FILE *file; char buffer[128]; file = fopen("C:/c/test.txt", "w"); if (file == NULL) < printf("Error opening file"); getch(); exit(ERROR_OPEN_FILE); >fprintf(file, "Hello, World!"); freopen("C:/c/test.txt", "r", file); if (file == NULL) < printf("Error opening file"); getch(); exit(ERROR_OPEN_FILE); >fgets(buffer, 127, file); printf("%s", buffer); fclose(file); getch(); >

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

. FILE *inputFile, *outputFile; unsigned m, n; unsigned i, j; inputFile = fopen(INPUT_FILE, READ_ONLY); if (inputFile == NULL) < printf("Error opening file %s", INPUT_FILE); getch(); exit(3); >outputFile = fopen(OUTPUT_FILE, WRITE_ONLY); if (outputFile == NULL) < printf("Error opening file %s", OUTPUT_FILE); getch(); if (inputFile != NULL) < fclose(inputFile); >exit(4); > .

В простых случаях можно действовать влоб, как в предыдущем куске кода. В более сложных случаях используются методы, подменяющиее RAII из С++: обёртки, или особенности компилятора (cleanup в GCC) и т.п.

Буферизация данных

  • 1) Если он заполнен
  • 2) Если поток закрывается
  • 3) Если мы явно указываем, что необходимо очистить буфер (здесь тоже есть исключения:)).
  • 4) Также очищается, если программа завершилась удачно. Вместе с этим закрываются и все файлы. В случае ошибки выполнения этого может не произойти.

Форсировать выгрузку буфера можно с помощью вызова функции fflush(File *). Рассмотрим два примера – с очисткой и без.

#include #include #include void main() < FILE *file; char c; file = fopen("C:/c/test.txt", "w"); do < c = getch(); fprintf(file, "%c", c); fprintf(stdout, "%c", c); //fflush(file); >while(c != 'q'); fclose(file); getch(); >

Раскомментируйте вызов fflush. Во время выполнения откройте текстовый файл и посмотрите на поведение.

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

void setbuf (FILE * stream, char * buffer);

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

int setvbuf ( FILE * stream, char * buffer, int mode, size_t size );
  • _IOFBF - полная буферизация. Данные записываются в файл, когда он заполняется. На считывание, буфер считается заполненным, когда запрашивается операция ввода и буфер пуст.
  • _IOLBF - линейная буферизация. Данные записываются в файл когда он заполняется, либо когда встречается символ новой строки. На считывание, буфер заполняется до символа новой строки, когда запрашивается операция ввода и буфер пуст.
  • _IONBF – без буферизации. В этом случае параметры size и buffer игнорируются.

Пример: зададим свой буфер и посмотрим, как осуществляется чтение из файла. Пусть файл короткий (что-нибудь, типа Hello, World!), и считываем мы его посимвольно

#include #include #include void main() < FILE *input = NULL; char c; char buffer[BUFSIZ * 2] = ; input = fopen("D:/c/text.txt", "rt"); setbuf(input, buffer); while (!feof(input)) < c = fgetc(input); printf("%c\n", c); printf("%s\n", buffer); _getch(); >fclose(input); >

Видно, что данные уже находятся в буфере. Считывание посимвольно производится уже из буфера.

feof

Функция int feof (FILE * stream); возвращает истину, если конец файла достигнут. Функцию удобно использовать, когда необходимо пройти весь файл от начала до конца. Пусть есть файл с текстовым содержимым text.txt. Считаем посимвольно файл и выведем на экран.

#include #include #include void main() < FILE *input = NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) < printf("Error opening file"); _getch(); exit(0); >while (!feof(input)) < c = fgetc(input); fprintf(stdout, "%c", c); >fclose(input); _getch(); >

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

#include #include #include void main() < FILE *input = NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) < printf("Error opening file"); _getch(); exit(0); >while (!feof(input)) < fscanf(input, "%c", &c); fprintf(stdout, "%c", c); >fclose(input); _getch(); >

Этот пример сработает с ошибкой (скорее всего) и выведет последний символ файла два раза.

Решение – не использовать feof. Например, хранить общее количество записей или использовать тот факт, что функции fscanf и пр. обычно возвращают число верно считанных и сопоставленных значений.

#include #include #include void main() < FILE *input = NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) < printf("Error opening file"); _getch(); exit(0); >while (fscanf(input, "%c", &c) == 1) < fprintf(stdout, "%c", c); >fclose(input); _getch(); >

Примеры

1. В одном файле записаны два числа - размерности массива. Заполним второй файл массивом случайных чисел.

#include #include #include #include //Имена файлов и права доступа #define INPUT_FILE "D:/c/input.txt" #define OUTPUT_FILE "D:/c/output.txt" #define READ_ONLY "r" #define WRITE_ONLY "w" //Максимальное значение для размера массива #define MAX_DIMENSION 100 //Ошибка при открытии файла #define ERROR_OPEN_FILE -3 void main() < FILE *inputFile, *outputFile; unsigned m, n; unsigned i, j; inputFile = fopen(INPUT_FILE, READ_ONLY); if (inputFile == NULL) < printf("Error opening file %s", INPUT_FILE); getch(); exit(ERROR_OPEN_FILE); >outputFile = fopen(OUTPUT_FILE, WRITE_ONLY); if (outputFile == NULL) < printf("Error opening file %s", OUTPUT_FILE); getch(); //Если файл для чтения удалось открыть, то его необходимо закрыть if (inputFile != NULL) < fclose(inputFile); >exit(ERROR_OPEN_FILE); > fscanf(inputFile, "%ud %ud", &m, &n); if (m > MAX_DIMENSION) < m = MAX_DIMENSION; >if (n > MAX_DIMENSION) < n = MAX_DIMENSION; >srand(time(NULL)); for (i = 0; i < n; i++) < for (j = 0; j < m; j++) < fprintf(outputFile, "%8d ", rand()); >fprintf(outputFile, "\n"); > //Закрываем файлы fclose(inputFile); fclose(outputFile); >

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

#include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *origin = NULL; FILE *output = NULL; char filename[1024]; int mode; printf("Enter filename: "); scanf("%1023s", filename); origin = fopen(filename, "r"); if (origin == NULL) < printf("Error opening file %s", filename); getch(); exit(ERROR_FILE_OPEN); >printf("enter mode: [1 - copy, 2 - print] "); scanf("%d", &mode); if (mode == 1) < printf("Enter filename: "); scanf("%1023s", filename); output = fopen(filename, "w"); if (output == NULL) < printf("Error opening file %s", filename); getch(); fclose(origin); exit(ERROR_FILE_OPEN); >> else < output = stdout; >while (!feof(origin)) < fprintf(output, "%c", fgetc(origin)); >fclose(origin); fclose(output); getch(); >

3. Пользователь вводит данные с консоли и они записываются в файл до тех пор, пока не будет нажата клавиша esc. Проверьте программу и посмотрите. как она себя ведёт в случае, если вы вводите backspace: что выводится в файл и что выводится на консоль.

#include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *output = NULL; char c; output = fopen("D:/c/test_output.txt", "w+t"); if (output == NULL) < printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); >for (;;) < c = _getch(); if (c == 27) < break; >fputc(c, output); fputc(c, stdout); > fclose(output); >

4. В файле записаны целые числа. Найти максимальное из них. Воспользуемся тем, что функция fscanf возвращает число верно прочитанных и сопоставленных объектов. Каждый раз должно возвращаться число 1.

#include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *input = NULL; int num, maxn, hasRead; input = fopen("D:/c/input.txt", "r"); if (input == NULL) < printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); >maxn = INT_MIN; hasRead = 1; while (hasRead == 1) < hasRead = fscanf(input, "%d", &num); if (hasRead != 1) < continue; >if (num > maxn) < maxn = num; >> printf("max number = %d", maxn); fclose(input); _getch(); >

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

#include #include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *input = NULL; int num, maxn, hasRead; input = fopen("D:/c/input.txt", "r"); if (input == NULL) < printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); >maxn = INT_MIN; while (!feof(input)) < fscanf(input, "%d", &num); if (num >maxn) < maxn = num; >> printf("max number = %d", maxn); fclose(input); _getch(); >

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

Файл с переводом выглядит примерно так

солнце sun
карандаш pen
шариковая ручка pencil
дверь door
окно windows
стул chair
кресло armchair

и сохранён в кодировке cp866 (OEM 866). При этом важно: последняя пара cлов также заканчивается переводом строки.

Алгоритм следующий - считываем строку из файла, находим в строке знак табуляции, подменяем знак табуляции нулём, копируем русское слово из буфера, копируем английское слово из буфера, проверяем на равенство.

#include #include #include #include #define ERROR_FILE_OPEN -3 void main() < FILE *input = NULL; char buffer[512]; char enWord[128]; char ruWord[128]; char usrWord[128]; unsigned index; int length; int wasFound; input = fopen("D:/c/input.txt", "r"); if (input == NULL) < printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); >printf("enter word: "); fgets(usrWord, 127, stdin); wasFound = 0; while (!feof(input)) < fgets(buffer, 511, input); length = strlen(buffer); for (index = 0; index < length; index++) < if (buffer[index] == '\t') < buffer[index] = '\0'; break; >> strcpy(ruWord, buffer); strcpy(enWord, &buffer[index + 1]); if (!strcmp(enWord, usrWord)) < wasFound = 1; break; >> if (wasFound) < printf("%s", ruWord); >else < printf("Word not found"); >fclose(input); _getch(); >

6. Подсчитать количество строк в файле. Будем считывать файл посимвольно, считая количество символов '\n' до тех пор, пока не встретим символ EOF. EOF – это спецсимвол, который указывает на то, что ввод закончен и больше нет данных для чтения. Функция возвращает отрицательное значение в случае ошибки.
ЗАМЕЧАНИЕ: EOF имеет тип int, поэтому нужно использовать int для считывания символов. Кроме того, значение EOF не определено стандартом.

#define _CRT_SECURE_NO_WARNINGS #include #include #include int cntLines(const char *filename) < int lines = 0; int any; //any типа int, потому что EOF имеет тип int! FILE *f = fopen(filename, "r"); if (f == NULL) < return -1; >do < any = fgetc(f); //printf("%c", any);//debug if (any == '\n') < lines++; >> while(any != EOF); fclose(f); return lines; > void main() < printf("%d\n", cntLines("C:/c/file.txt")); _getch(); >

ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students

email

Всё ещё не понятно? – пиши вопросы на ящик

Как считать весь файл в строку c

При работе с текстовыми файлами граздо проще работать с данными не как с отдельными символами, а как со строками с помощью функций fgets() и fputs() .

Запись текстового файла

Функция fputs() записывает в файл строку, то есть набор символов, который завершается символом '\0'. Она имеет следующий прототип:

int fputs(const char *s, FILE *stream);

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

При записи строки нулевой символ '\0' в файл не записывается.

Например, запишем строку в файл:

#include int main(void) < // строка для записи char * message = "Hello METANIT.COM!\nAn apple a day keeps the doctor away"; // файл для записи char * filename = "data.txt"; // запись в файл FILE *fp = fopen(filename, "w"); if(fp) < // записываем строку fputs(message, fp); fclose(fp); printf("File has been written\n"); >>

Запись довольно проста: открываем файл на запись и с помощью вызова fputs(message, fp) записываем в файл искомую строку.

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

Функция fgets() считывает из файла одну строку и имеет следующий прототип:

char * fgets(char *s, int n, FILE *sream);

Первый параметр char *s представляет строку, в которую производится считывание. Второй параметр - число n указывает, сколько символов необходимо считывать. И третий параметр представляет файловый поток, из которого производится считывание.

При вызове функция считывает из файла не более n-1 символов. Функция прекращает чтение, когда прочтет n-1 символов или встретит символ переноса строки \n. Все считанные символы записываются в строку s, в том числе символ \n. И также конец каждой строки дописывается нулевой символ '\0'.

При успешном завершении функция возвращает указатель s , а при ошибке или достижении конца файла возвращается значение NULL .

Считаем данные из выше записанного файла "data.txt":

#include int main(void) < // файл чтения char * filename = "data.txt"; // буфер для считавания данных из файла char buffer[256]; // чтение из файла FILE *fp = fopen(filename, "r"); if(fp) < // пока не дойдем до конца, считываем по 256 байт while((fgets(buffer, 256, fp))!=NULL) < printf("%s", buffer); >fclose(fp); > >

Здеcь открываем файл на чтение и в цикле считываем из файла по 256 символов и выводим их на консоль с помощью вызова fgets(cc, 256, fp) . Когда данные в файле закончатся, функция возвратит NULL, и произойдет выход из цикла.

Копирование файлов

Другой пример работы с текстовыми файлами - копирование содержимого из одного файла в другой:

#include int main(void) < char * filename1 = "data1.txt"; char * filename2 = "data2.txt"; char buffer[256]; FILE *f1 = fopen(filename1, "r"); // файл на чтение FILE *f2 = fopen(filename2, "w"); // файл на запись if(!f1 || !f2) < printf("Error occured while opening file\n"); >else < // пока не дойдем до конца, считываем по 256 байт из файла f1 while((fgets(buffer, 256, f1))!=NULL) < // записываем строку в файл f2 fputs(buffer, f2); printf("%s", buffer); >> fclose(f1); fclose(f2); return 0; >

Как прочитать содержимое файла в string

Ещё несколько вариантов (в теории, по возрастанию скорости):

std::string readFile(const std::string& fileName) < return std::string((std::istreambuf_iterator(std::ifstream(fileName))), std::istreambuf_iterator()); > 
std::string readFile(const std::string& fileName)
std::string readFile(const std::string& fileName) < std::ifstream f(fileName); f.seekg(0, std::ios::end); size_t size = f.tellg(); std::string s(size, ' '); f.seekg(0); f.read(&s[0], size); // по стандарту можно в C++11, по факту работает и на старых компиляторах return s; >

Отслеживать
ответ дан 26 мая 2016 в 0:21
Vladimir Gamalyan Vladimir Gamalyan
7,733 5 5 золотых знаков 28 28 серебряных знаков 57 57 бронзовых знаков

последний вариант дурной какой-то - зачем забивать строку пробелами (std::string s(size, ' ')), когда можно просто сделать .resize()?

26 мая 2016 в 3:27

@gbg можно и resize , разница в том, что resize потребует реаллокации памяти, а fill конструктор (вероятнее всего) сразу запросит нужный кусок. Другими словами resize потенциально медленнее.

26 мая 2016 в 7:36

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

26 мая 2016 в 8:53
@gbg вы удивитесь, но resize делает то же самое (забивает нулями)
26 мая 2016 в 8:54
вы удивитесь, но resize конструктор не вызывает stackoverflow.com/a/7413968/5533854
26 мая 2016 в 8:57

Для начала, ofstream предназначен для вывода в файл, для ввода из файла используйте ifstream . Проще всего читать построчно:

std::ifstream file("тут ваш путь"); std::string line; while (std::getline(file, line)) < // обработайте строку // например, добавьте её к суммарной строке >

Не забудьте, что std::getline не сохраняет \n в конце строки, так что при необходимости добавьте его сами.

Чтение текстового файла построчно на C#

Два самых простых способа как прочитать текстовый файл построчно на C#. В обоих случаях используется using System.IO;. Напишу сразу примеры кода без всяких прелюдий.

Способ 1

Классический вариант чтения файла построчно:

StreamReader f = new StreamReader("test.txt"); while (!f.EndOfStream) < string s = f.ReadLine(); // что-нибудь делаем с прочитанной строкой s >f.Close();

Еще один пример, как можно организовать цикл построчного чтения:

string s; StreamReader f = new StreamReader("test.txt"); while ((s = f.ReadLine()) != null) < // что-нибудь делаем с прочитанной строкой s >f.Close();

Указать кодировку при чтении файла (например, DOS-кодировку) можно так:

StreamReader f = new StreamReader("test.txt", Encoding.GetEncoding(866));

Так же можно использовать обёртку using чтобы не закрывать файл после чтения.

string s; using (var f = new StreamReader("test.txt", Encoding.GetEncoding(1251))) < while ((s = f.ReadLine()) != null) < // что-нибудь делаем с прочитанной строкой s >>

Способ 2

Прочитать весь файл целиком в массив строк:

string[] lines = File.ReadAllLines("test.txt"); foreach (string s in lines) < // что-нибудь делаем с прочитанной строкой s >

По аналогии, кстати, можно и записывать построчно в файл, использовав StreamWriter, f.WriteLine и File.WriteAllLines. Побробнее смотрите в следующей статье.

Ввод данных из файла и вывод в файл в языке программирования С

До этого при вводе-выводе данных мы работали со стандартными потоками — клавиатурой и монитором. Теперь рассмотрим, как в Си реализовано получение данных из файлов и запись их туда. Перед тем как выполнять эти операции, надо открыть файл и получить доступ к нему.

В языке программирования C указатель на файл имеет тип FILE и его объявление выглядит так:

FILE *myfile;

С другой стороны, функция fopen() открывает файл по указанному в качестве первого аргумента адресу в режиме чтения "r", записи "w" или добавления "a" и возвращает в программу указатель на него. Поэтому процесс открытия файла и подключения его к программе выглядит примерно так:

myfile = fopen("hello.txt", "r");

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

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

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

Объявление функции fopen содержится в заголовочном файле stdio.h , поэтому требуется его подключение. Также в stdio.h объявлен тип-структура FILE .

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

fclose(myfile);

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

Чтение из текстового файла и запись в него

fscanf

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

fscanf(myfile, "%s%d", str, &a); 

Возвращает количество удачно считанных данных или EOF. Пробелы, символы перехода на новую строку учитываются как разделители данных.

Допустим, у нас есть файл содержащий такое описание объектов:

apples 10 23.4 bananas 5 25.0 bread 1 10.3

Тогда, чтобы считать эти данные, мы можем написать такую программу:

#include int main () { FILE *file; struct food { char name[20]; unsigned qty; float price; }; struct food shop[10]; char i = 0; file = fopen("goods.txt", "r"); while (fscanf(file, "%s%u%f", shop[i].name, &(shop[i].qty), &(shop[i].price)) != EOF) { printf("%s %u %.2f\n", shop[i].name, shop[i].qty, shop[i].price); i++; } }

В данном случае объявляется структура и массив структур. Каждая строка из файла соответствует одному элементу массива; элемент массива представляет собой структуру, содержащую строковое и два числовых поля. За одну итерацию цикл считывает одну строку. Когда встречается конец файла fscanf() возвращает значение EOF и цикл завершается.

fgets

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

fgets(массив_символов, количество_считываемых_символов, указатель_на_файл)
fgets(str, 50, myfile)

Такой вызов функции прочитает из файла, связанного с указателем myfile , одну строку текста полностью, если ее длина меньше 50 символов с учетом символа '\n', который функция также сохранит в массиве. Последним (50-ым) элементом массива str будет символ '\0', добавленный fgets . Если строка окажется длиннее, то функция прочитает 49 символов и в конце запишет '\0'. В таком случае '\n' в считанной строке содержаться не будет.

#include #define N 80 int main() { FILE *file; char arr[N]; file = fopen("goods.txt", "r"); while (fgets(arr, N, file) != NULL) printf("%s", arr); printf("\n"); fclose(file); }

В этой программе в отличие от предыдущей данные считываются строка за строкой в массив arr . Когда считывается следующая строка, предыдущая теряется. Функция fgets() возвращает NULL в случае, если не может прочитать следующую строку.

getc или fgetc

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

#include #define N 80 int main() { FILE *file; char str[N]; int i; file = fopen("goods.txt", "r"); while ((str[i] = fgetc(file)) != EOF) { if (str[i] == '\n') { str[i] = '\0'; printf("%s\n", str); i = 0; } else i++; } str[i] = '\0'; printf("%s\n",str); fclose(file); }

Приведенный в качестве примера код выводит данные из файла на экран.

Запись в текстовый файл

Также как и ввод, вывод в файл может быть различным.

  • Форматированный вывод. Функция fprintf( файловый_указатель, строка_формата, переменные ) .
  • Построчный вывод. Функция fputs( строка, файловый_указатель ) .
  • Посимвольный вывод. Функция fputc() или putc( символ, файловый_указатель ) .

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

Запись в каждую строку файла полей одной структуры (прерывание в Linux ‒ Ctrl + D ):

#include #define N 80 int main() { FILE *file; char str[N]; int i; file = fopen("goods.txt", "r"); while ((str[i] = fgetc(file)) != EOF) { if (str[i] == '\n') { str[i] = '\0'; printf("%s\n", str); i = 0; } else i++; } str[i] = '\0'; printf("%s\n",str); fclose(file); }

Построчный вывод в файл ( fputs() , в отличие от puts() сама не помещает в конце строки '\n'):

#include int main () { FILE *file; char str[10]; file = fopen("output.txt", "w"); while (scanf("%s", str) != EOF) { fputs(str, file); fputs("\n", file); } fclose(file); }

Пример посимвольного вывода:

#include int main() { FILE *file; char i; file = fopen("output.txt", "w"); while ((i = getchar()) != EOF) fputc(i, file); fclose(file); }

Чтение из двоичного файла и запись в него

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

При открытии файла для двоичного доступа, вторым аргументом fopen() является строка "rb" или "wb".

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

Функции fread() и fwrite() имеют следующие параметры:

  1. адрес области памяти, куда данные записываются или откуда считываются,
  2. размер одного данного какого-либо типа,
  3. количество считываемых данных указанного размера,
  4. файловый указатель.

Эти функции возвращают количество успешно прочитанных или записанных данных. Т.е. можно "заказать" считывание 50 элементов данных, а получить только 10. Ошибки при этом не возникнет.

Пример использования функций fread() и fwrite() :

#include #include int main() { FILE *file; char shelf1[50], shelf2[100]; int n, m; file = fopen("shelf1.txt", "rb"); n = fread(shelf1, sizeof(char), 50, file); fclose(file); file = fopen("shelf2.txt", "rb"); m = fread(shelf2, sizeof(char), 50, file); fclose(file); shelf1[n] = '\0'; shelf2[m] = '\n'; shelf2[m+1] = '\0'; file = fopen("shop.txt", "wb"); fwrite(strcat(shelf2, shelf1), sizeof(char), n+m, file); fclose(file); }

Здесь осуществляется попытка чтения из первого файла 50-ти символов. В n сохраняется количество реально считанных символов. Значение n может быть равно 50 или меньше. Данные помещаются в строку. То же самое происходит со вторым файлом. Далее первая строка присоединяется ко второй, и данные сбрасываются в третий файл.

Курс с решением задач:
pdf-версия

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *