C как читать текст из файла

Обновлено: 21.11.2024

В этом руководстве вы узнаете об обработке файлов в C. Вы научитесь обрабатывать стандартный ввод-вывод в C с помощью функций fprintf(), fscanf(), fread(), fwrite(), fseek() и т. д. помощь примеров.

Файл — это контейнер на компьютерных запоминающих устройствах, используемый для хранения данных.

Зачем нужны файлы?

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

Типы файлов

При работе с файлами необходимо знать два типа файлов:

1. Текстовые файлы

Текстовые файлы — это обычные файлы .txt. Вы можете легко создавать текстовые файлы с помощью любых простых текстовых редакторов, таких как Блокнот.

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

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

2. Бинарные файлы

Двоичные файлы — это в основном файлы .bin на вашем компьютере.

Вместо того, чтобы хранить данные в виде обычного текста, они хранят их в двоичной форме (0 и 1).

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

Операции с файлами

В C вы можете выполнять четыре основные операции с файлами, текстовыми или двоичными:

  1. Создание нового файла
  2. Открытие существующего файла
  3. Закрытие файла
  4. Чтение и запись информации в файл

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

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

Открытие файла — для создания и редактирования

Открытие файла выполняется с помощью функции fopen(), определенной в заголовочном файле stdio.h.

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

  • Предположим, что файл newprogram.txt не существует в папке E:\cprogram. Первая функция создает новый файл с именем newprogram.txt и открывает его для записи в соответствии с режимом 'w'.
    Режим записи позволяет создавать и редактировать (перезаписывать) содержимое файла.
  • Теперь предположим, что второй двоичный файл oldprogram.bin существует в папке E:\cprogram. Вторая функция открывает существующий файл для чтения в бинарном режиме 'rb'.
    Режим чтения позволяет только читать файл, вы не можете записывать в файл.

Закрытие файла

Файл (как текстовый, так и двоичный) должен быть закрыт после чтения/записи.

Закрытие файла выполняется с помощью функции fclose().

Здесь fptr — это указатель файла, связанный с файлом, который нужно закрыть.

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

Для чтения и записи в текстовый файл мы используем функции fprintf() и fscanf().

Это просто версии файлов printf() и scanf() . Единственное отличие состоит в том, что fprintf() и fscanf() ожидают указатель на структуру FILE.

Пример 1. Запись в текстовый файл

Эта программа получает номер от пользователя и сохраняет его в файле program.txt .

После того, как вы скомпилируете и запустите эту программу, вы увидите текстовый файл program.txt, созданный на диске C вашего компьютера. Когда вы откроете файл, вы увидите введенное целое число.

Пример 2. Чтение из текстового файла

Эта программа считывает целое число из файла program.txt и выводит его на экран.

Если вы успешно создали файл из примера 1, запустив эту программу, вы получите введенное целое число.

Другие функции, такие как fgetchar() , fputc() и т. д., можно использовать аналогичным образом.

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

Функции fread() и fwrite() используются для чтения и записи в файл на диске соответственно в случае двоичных файлов.

Запись в двоичный файл

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

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

Пример 3. Запись в двоичный файл с помощью fwrite()

В этой программе мы создаем новый файл program.bin на диске C.

Мы объявляем структуру threeNum с тремя числами — n1, n2 и n3 и определяем ее в основной функции как num.

Теперь внутри цикла for мы сохраняем значение в файле с помощью fwrite() .

Первый параметр принимает адрес num, а второй параметр принимает размер структуры threeNum .

Поскольку мы вставляем только один экземпляр num , третий параметр равен 1 . И последний параметр *fptr указывает на файл, в котором мы сохраняем данные.

Наконец, мы закрываем файл.

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

Функция fread() также принимает 4 аргумента, как и функция fwrite(), описанная выше.

Пример 4. Чтение из двоичного файла с помощью функции fread()

В этой программе вы читаете один и тот же файл program.bin и перебираете записи одну за другой.

Проще говоря, вы читаете одну запись threeNum размера threeNum из файла, на который указывает *fptr, в структуру num .

Вы получите те же записи, что и в примере 3.

Получение данных с помощью fseek()

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

Это приведет к потере большого количества памяти и рабочего времени. Более простой способ получить необходимые данные можно с помощью fseek() .

Как следует из названия, fseek() ищет курсор для данной записи в файле.

Синтаксис fseek()

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

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

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

Открытие файлов

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

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

Открывает существующий текстовый файл для чтения.

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

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

Открывает текстовый файл как для чтения, так и для записи.

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

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

Если вы собираетесь работать с бинарными файлами, вы будете использовать следующие режимы доступа вместо упомянутых выше —

Закрытие файла

Чтобы закрыть файл, используйте функцию fclose(). Прототип этой функции —

Функция fclose(-) возвращает ноль в случае успеха или EOF, если при закрытии файла возникла ошибка. Эта функция фактически сбрасывает все данные, все еще ожидающие в буфере, в файл, закрывает файл и освобождает всю память, используемую для файла. EOF — это константа, определенная в заголовочном файле stdio.h.

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

Запись файла

Ниже приведена простейшая функция для записи отдельных символов в поток —

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

Функция fputs() записывает строку s в выходной поток, на который ссылается fp. В случае успеха он возвращает неотрицательное значение, в противном случае в случае какой-либо ошибки возвращается EOF. Вы также можете использовать функцию int fprintf(FILE *fp,const char *format, . ) для записи строки в файл. Попробуйте следующий пример.

Убедитесь, что у вас есть каталог /tmp. Если это не так, то прежде чем продолжить, вы должны создать этот каталог на своем компьютере.

Когда приведенный выше код скомпилирован и выполнен, он создает новый файл test.txt в каталоге /tmp и записывает две строки, используя две разные функции. Давайте прочитаем этот файл в следующем разделе.

Чтение файла

Ниже приведена простейшая функция для чтения одного символа из файла —

Функция fgetc() считывает символ из входного файла, на который ссылается fp. Возвращаемое значение — это прочитанный символ, или в случае какой-либо ошибки он возвращает EOF. Следующая функция позволяет прочитать строку из потока —

Функция fgets() считывает до n-1 символов из входного потока, на который ссылается fp. Он копирует прочитанную строку в буфер buf, добавляя нулевой символ для завершения строки.

Если эта функция встречает символ новой строки '\n' или конец файла EOF до того, как будет прочитано максимальное количество символов, она возвращает только символы, прочитанные до этой точки, включая символ новой строки. Вы также можете использовать функцию int fscanf(FILE *fp, const char *format, . ) для чтения строк из файла, но она останавливает чтение после обнаружения первого символа пробела.

Когда приведенный выше код компилируется и выполняется, он считывает файл, созданный в предыдущем разделе, и выдает следующий результат —

Давайте немного подробнее рассмотрим, что здесь произошло. Во-первых, fscanf() считывает только это, потому что после этого он столкнулся с пробелом, второй вызов для fgets(), который читает оставшуюся строку, пока не встретит конец строки. Наконец, последний вызов fgets() полностью считывает вторую строку.

Двоичные функции ввода/вывода

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

Обе эти функции следует использовать для чтения или записи блоков памяти — обычно это массивы или структуры.

fgetc() считывает символы, на которые указывает указатель функции в это время. При каждом успешном чтении он возвращает символ (значение ASCII), считанный из потока, и перемещает позицию чтения к следующему символу. Эта функция возвращает константу EOF (-1), если нет содержимого для чтения или чтение завершилось неудачно.

Синтаксис:

  • Эта программа считывает все содержимое файла, используя эту функцию, считывая символы один за другим.
  • Будет использоваться цикл Do-While, который будет считывать символ до тех пор, пока он не достигнет файла.
  • Когда он достигает конца, он возвращает символ EOF (-1).

Использование EOF:
Ниже приведена программа на C для реализации описанного выше подхода

Входной файл:

Вывод:

В приведенном выше коде подход заключается в том, чтобы прочитать один символ из файла и проверить, не является ли он EOF, если это не так, то распечатать его, а если это так, то прекратить чтение.

Использование feof():
функция feof() принимает в качестве аргумента указатель на файл и возвращает значение true, если указатель достигает конца файла.

Синтаксис:

  • В этом подходе символ считывается с помощью fgetc().
  • Использование функции feof() для проверки конца файла. так как feof() возвращает true после достижения конца.
  • Используйте логический оператор НЕ(!), чтобы при достижении конечного условия становилось ложным и цикл останавливался.

Ниже приведена программа на C для реализации описанного выше подхода:

Входной файл:

Вывод:

fgets()

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

Синтаксис:

char * fgets(char *str, int size, FILE * ptr);

Здесь,
str: это строка, в которой fgets() сохраняет строку после чтения из файла.< br />размер: максимальное количество символов для чтения из потока.
ptr: указатель на файл.

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

Ниже приведена программа на C для реализации описанного выше подхода:

Входной файл:

Вывод:

fscanf()

fscanf() считывает форматированный ввод из потока.

Синтаксис:

int fscanf(FILE *ptr, const char *format, …)

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

Вывод:

fread()

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

Синтаксис:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

ptr: это указатель на блок памяти с минимальным размером size*nmemb байт.
size: это размер в байтах каждого элемента для чтения.
nmemb: это количество элементов, каждый из которых имеет размер size байт.
stream: это указатель на FILE, указывающий входной поток.

Я хочу написать программу на C, которая сможет читать этот файл и выводить содержимое на консоль (предположим, что файл содержит только текст ASCII).

Я не знаю, как получить размер моей строковой переменной. Вот так:

Размер 999 не работает, поскольку строка, возвращаемая fscanf, может быть больше. Как я могу это решить?

9 ответов 9

Самый простой способ — прочитать символ и распечатать его сразу после прочтения:

c — это int выше, так как EOF — это отрицательное число, а обычный char может быть беззнаковым .

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

Второй описанный выше метод, по сути, представляет собой то, как вы будете читать файл с динамически выделенным массивом:

Ваш метод fscanf() с форматом %s теряет информацию о пробелах в файле, поэтому он не совсем копирует файл в стандартный вывод.

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

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

@overexchange Вопрос не о строках, а о чтении файла и копировании его содержимого в стандартный вывод.

@shjeff Файл не может содержать символ EOF. Обратите внимание, что c — это int, и C гарантирует, что EOF не равен ни одному допустимому символу.

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

Я не говорю, что это лучше. Это не так, и, как у Рикардо, иногда это может быть плохо, но я считаю, что это хорошее решение для простых случаев.

Я посыпал его комментариями, потому что происходит много всего.

Дайте мне знать, если это полезно или вы могли бы чему-то научиться :)

Разве он не должен читать buffer[string_size] = '\0'; вместо string_size+1? На самом деле, фактическая строка изменяется от 0 до string_size-1, и поэтому символ \0 должен быть равен string_size , верно?

Обратите внимание, что в Windows string_size может не совпадать с read_size, если файл открыт в текстовом режиме. Я цитирую эту ссылку (bytes.com/topic/c/answers/479976-interesting-thing-about-fread). Под Windows текстовый файл имеет два символа для конца строки (CR+LF -- "\r \n"), но библиотека C удалит CR при чтении файла, открытого в текстовом режиме, так что программа будет видеть только LF ('\n'). Вызов stat() возвращает "реальный" размер, если файл, в то время как возвраты от fread() лишили CR.

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

Используйте "read()" вместо fscanf:

ОПИСАНИЕ

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

< /цитата>

Вот пример:

Рабочая часть из этого примера:

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

Вы можете использовать fgets и ограничить размер считываемой строки.

Вы можете изменить время в коде на:

На ум приходят два подхода.

Во-первых, не используйте scanf . Используйте функцию fgets(), которая принимает параметр для указания размера буфера и оставляет без изменений все символы новой строки. Простой цикл над файлом, который печатает содержимое буфера, должен естественным образом скопировать файл без изменений.

Во-вторых, используйте fread() или распространенную идиому C с fgetc() . Они будут обрабатывать файл фрагментами фиксированного размера или по одному символу за раз.

Если вы должны обработать файл через строки, разделенные пробелами, используйте fgets или fread для чтения файла и что-то вроде strtok для разделения буфера по пробелам. Не забудьте обработать переход от одного буфера к другому, так как ваши целевые строки, скорее всего, охватывают границу буфера.

Если есть внешнее требование использовать scanf для чтения, ограничьте длину строки, которую он может прочитать, с помощью поля точности в спецификаторе формата. В вашем случае с 999-байтовым буфером скажите scanf("%998s", str); который запишет не более 998 символов в буфер, оставив место для нулевого терминатора. Если разрешены отдельные строки длиннее вашего буфера, вам придется обрабатывать их двумя частями.Если нет, у вас есть возможность вежливо сообщить пользователю об ошибке, не создавая дыры в безопасности переполнения буфера.

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

Введение в программирование на C/C++
Основной ввод и вывод текстовых файлов

Хранение двоичных и текстовых данных

  • Компьютеры обычно хранят числовые данные в двоичном формате, который подходит для обработки и хранения с помощью компьютерных микросхем, но не может быть прочитан людьми.
  • Текстовые представления данных состоят из ряда печатных символов, которые люди интерпретируют как числа, но которые компьютер не может напрямую использовать для расчетов.
  • Например, оператор "float pi = acos(-1.0);" будет выделять 32 бита двоичной памяти для числа с плавающей запятой и сохранять в этой памяти двоичное представление числа пи, обычно 1 знаковый бит плюс 23 бита информации о мантиссе (цифре) плюс 8 бит для хранения (двоичной) экспоненты. Компьютер может манипулировать этими данными для таких операций, как умножение и деление, но человек не сможет понять значение битов.
  • С другой стороны, если мы используем printf для вывода переменной на экран, то функция printf сгенерирует ряд символов, понятных человеку, и отправит их на экран. Например, компьютер может сгенерировать последовательность символов: «3», «.», «1», «4», «1», «5», «9». Эти семь символов по 8 бит каждый будут занимать в общей сложности 56 бит, что почти вдвое превышает общий объем памяти двоичного эквивалента.
  • Если фактические данные, которые необходимо сохранить, представляют собой текстовые символы, например простой документ, то уместно хранение в текстовом формате.
    • ( Если файл также не содержит двоичную информацию, такую ​​как информация о форматировании или изображения, в этом случае двоичный файл необходим. )

    Основы чтения и записи текстовых файлов

    1. Откройте файл с помощью fopen
    2. Чтение из файла с помощью fscanf или запись в файл с помощью fprintf
    3. Закройте файл с помощью fclose.

    открыть

    • Прежде чем файл можно будет использовать, его необходимо открыть. Одной из команд для этого является системный вызов fopen, у которого есть прототип:
      • Аргумент пути — это просто имя файла, который вы хотите открыть, либо в виде "строки в кавычках", либо в виде массива символов, содержащего имя файла, заканчивающееся нулевым байтом.
      • Режим указывает, следует ли открывать файл для чтения или записи (или для некоторых других вещей, о которых мы сейчас не будем беспокоиться), и выражается в виде строки в кавычках, например "r".
      • На данный момент нас интересуют только следующие режимы: r Открыть текстовый файл для чтения. Поток располагается в начале файла. r+ Открыт для чтения и записи. Поток располагается в начале файла. w Обрезать файл до нулевой длины или создать текстовый файл для записи. Поток располагается в начале файла. w+ Открыт для чтения и записи. Файл создается, если он не существует, в противном случае он усекается. Поток располагается в начале файла. a Открыть для добавления (запись в конец файла). Файл создается, если он не существует. Поток располагается в конце файла. a+ Открыть для чтения и добавления (запись в конец файла). Файл создается, если он не существует. Исходной позицией файла для чтения является начало файла, но выходные данные всегда добавляются в конец файла.
      • Создайте переменную типа FILE * и сохраните в ней значение, возвращаемое fopen.
      • Перед использованием проверьте возвращаемое значение, чтобы убедиться, что оно не равно NULL. Если fopen возвращает NULL, значит по какой-то причине не удалось открыть запрошенный файл.
      • ФАЙЛ *, возвращаемый fopen, потребуется позже, чтобы использовать файл.
      • Есть три предопределенных FILE *, которые особенно полезны:
        • stdin — стандартный ввод, обычно с клавиатуры.
        • stdout — стандартный вывод, обычно экран. (Хотя довольно часто стандартный вывод перенаправляется в файл для последующего использования.)
        • stderr — стандартная ошибка, также нормально отображается экран. Преимущество наличия как stdout, так и stderr заключается в том, что если «результаты» программы, записанные в stdout, перенаправляются в файл, то сообщения, записанные в stderr, по-прежнему отправляются на экран.

        fprintf и fscanf

        • fprintf и fscanf работают точно так же, как printf и scanf, за исключением того, что они принимают в начале дополнительный аргумент типа FILE *:

        int fprintf(ФАЙЛ *поток, const char *формат, . );

        int fscanf(ФАЙЛ * поток, const char *формат, .);

        Читайте также: