Какая функция описанная в заголовочном файле читает строку символов из файла
Доброго времени суток! Сегодня мы поговорим о строках и символах в Си, и о том, как они передаются в файл.
Сначала немного теории. До этого момента мы уже разбирали, как передавать переменную на вывод с помощью спецификаторов формата (%d, %c и т.д.). А также рассматривали различные методы открытия файлов для записи и чтения. Теперь давайте обобщим все варианты открытия файла и, что значит каждое обозначение.
Режимы чтения и записи файлов
В Си, для открытия файла, используется команда fopen(), в аргументах которого записывается имя файла и обозначение режима записи:
Как пример: file = fopen("file.tx) .
Спецификаторы
Повторим основную часть для функции scanf():
%c Читает одиночные символы
%d Читает десятичное число
%f Читает число с плавающей запятой
%s Читает строку
Другие функции считывания разберём уже на примерах.
Задачи на запись и считывание строк и символов в Си
Теперь, когда мы всё обобщили приступим к задачам на символы и строки.
Записать в текстовый файл на разных строках 5 слов и 3 числа, закрыть его, а потом дописать туда еще 2 символа.
Данная несложная задача проверяет нас на знание тех самых режимов открытия файлов, о которых мы говорили ранее.
Для работы со строками в Си предусмотрены разнообразные библиотеки с различными функциями. В стандартной библиотеке список таких функций скромен, но в большинстве случаев этого достаточно. gets() аналог функции scanf(), однако, работает он только со строкой. Записывает в указанный аргумент сканируемую им строку из консоли ввода в формате символов.
На самом деле и обычные строки следует воспринимать как обычные одномерные массивы с форматом char. Также следует учесть, что размер массива (у нас char str[200]) должен быть не меньше чем строка, а лучше больше.
Произведена запись в файл считанных с консоли строки и цифр. И закрыли файл. ВСЕГДА закрывайте файл после открытия. иначе это может повлиять на работоспособность вашего компьютера.
Создать вручную англоязычный текстовый файл небольшого объема. Прочитать файл посимвольно и вывести на экран все символы из файла. Пропустив строку, вывести на экран первую и последнюю букву каждой строки в файле.
Так как предстоит более сложная работа со строками, сразу подключим библиотеку string.h
Создать вручную англоязычный текстовый файл небольшого объема. Найти, сколько всего гласных содержится в этом файле. Найти количество гласных в каждой строке файла. Результаты дописать в этот же файл на новой строке.
Перво-наперво, посчитаем количество гласных во всём файле с помощью счётчика vse.
Теперь посчитаем количество строк, чтобы создать одномерный массив с количеством элементов равным количеству строк.
Теперь просто записываем требующиеся по заданию показания счётчиков в конец файла. Как и в случае закрытия файлов, так и при работе с malloc ВСЕГДА освобождайте память в конце программы с помощью команды free() , где в аргументах имя переменной.
На сегодня всё, если остались вопросы, пишите в комментариях. Вот исходники:
Добрый день! В этой статье я расскажу о том, как написать программу, которая будет считывать строки из файла. Покажу как записать их в массив или вывести. При написании программы будут использоваться функции из стандартной библиотеки языка C++.
Стандартная библиотека языка C++ <fstream> включает множество функций для работы с файлами. Описание функций можно найти в сети или в учебниках по C++. Здесь же я опишу одну, которая позволит произвести чтение строки из файла.
Содержание файла strings.txt
Три строки, содержащиеся в файле, я запишу массив и выведу на экран.
Пингвин читает содержимое файла
Переходим к написанию программы на C++.
Нашей программе понадобятся два заголовочных файла <iostream> и <fstream>. Первый нужен будет для использования вывода на консоль, второй для работы с файлами.
Объявим две целочисленные константы len и strings, они будут хранить максимальную длину наших строк и количество строк. Объявим символьную константу ch, которая будет хранить разделяющий символ. Первые две константы будут использоваться для объявления массива. Мой файл содержит 3 строки
При помощи значений двух первых констант объявим двумерный массив символов.
Создадим объект класса ostream, в конструктор поместим адрес файла, флаги открытия.
Флаг ios::in позволяет открыть файл для считывания, ios::binary открывает его в двоичном режиме.
Далее стоит проверить открылся ли файл, если не открылся, то завершаем работу программы.
В данный момент программа имеет такой вид
Теперь остается описать алгоритм считывания строк из файла и занесения их в массив с последующим выводом. Для этого понадобится цикл от нуля до strings с инкрементом переменной r. Во время каждого прохода цикла используем перегруженную функцию getline() объекта fs с тремя аргументами.
fs.getline(Массив_символов, Макс_длина_строки, Разделитель_строк)
Функция считывает символы из файла, пока не считает количество равное Макс_длина_строки, либо не встретит на своём пути символ равный Разделитель_строк, а после записывает считанные символы в Массив_символов. В качестве разделителя в моём текстовом файле используется перенос строки.
После сразу же выводим содержимое строки, хранящееся в массиве, при помощи поточного вывода в консоль cout.
Весь листинг конечной программы
За счет константных переменных её можно легко модернизировать. Изменив константу strings, можно указать количество выводимых строк. Чтение из файла будет производится до тех пор, пока массив не заполнится нужным количеством строк. Изменив константу ch, можно изменить разделитель строк(Например, можно разделять их пробелом и занести в массив отдельные слова из файла и т.д.).
Если Вас интересует запись в файл, то почитайте статью о чтении из input.txt и записи данных в файл output.txt.
Пример работы string и пример считывания всех строк из файла в массив с последующим выводом
БлогNot. Лекции по C/C++: работа с файлами (stdio.h)
Лекции по C/C++: работа с файлами (stdio.h)
В лекции рассмотрен классический способ работы с файлами в C/C++, основанный на библиотеке stdio.h (она же cstdio ) и доступе к данным через структуру FILE . Альтернативный современный механизм работы с файлами в языке C++ на основе потоков и библиотек <fstream> , <ifstream> , <ofstream> будет изучен в следующей лекции.
Базовые функции для работы с файлами описаны в библиотеке stdio.h . Вся работа с файлом выполняется через файловую переменную - указатель на структуру типа FILE , определённую в стандартной библиотеке:
Открыть файл можно функцией fopen , имеющей 2 параметра:
Параметр имя_файла может содержать относительный или абсолютный путь к открываемому файлу:
1) "data.txt" - открывается файл data.txt из текущей папки
Важно: при запуске exe-файла "текущая папка" – та, где он находится; при отладке в IDE папка может быть иной, например, в Visual Studio при открытом консольном решении с именем Console файл следует разместить в папке Console/Console , а при запуске исполняемого файла не из IDE – в папке Console/Debug .
2) "f:\\my.dat" - открывается файл my.dat из головной папки диска f:
3) имя файла запрашивается у пользователя:
Параметр режим_доступа определяет, какие действия будут разрешены с открываемым файлом, примеры его возможных значений:
1) "rt" - открываем для чтения текстовый файл;
2) "r+b" - открываем для произвольного доступа (чтение и запись) бинарный файл;
3) "at" – открываем текстовый файл для добавления данных в конец файла;
4) "w" - открываем файл для записи без указания того, текстовый он или бинарный.
Фактически, указание "r" или "t" не накладывает каких-либо ограничений на методы, которые мы будем применять для чтения или записи данных.
После открытия файла следует обязательно проверить, удалась ли эта операция. Для этого есть 2 основных подхода:
1) стандартный обработчик ferror (см. пособиe, п.8.7);
2) сравнить указатель, который вернула fopen , с константой NULL ( nullptr ) из стандартной библиотеки:
Пример. Приложение проверяет, удалось ли открыть файл из текущей папки, имя файла запрашивается у пользователя (Visual Studio)
Важно! Функции, возвращающие указатель, в том числе, fopen , считаются небезопасными в ряде новых компиляторов, например, Visual Studio 2015. Если их использование приводит не просто к предупреждению, а к генерации ошибок, есть 2 основных способа решения проблемы:
1) в соответствии с рекомендациями компилятора, заменить старые названия функций на их безопасные версии, например, strcpy на strcpy_s и fopen на fopen_s . При этом может измениться и способ вызова функций, например,
Если используется предкомпиляция, то можно определить этот макрос в заголовочном файле stdafx.h .
Выбор способа чтения или записи данных зависит от того, какой должна быть структура файла.
- fscanf - для чтения
- fprintf - для записи
Первым параметром этих функций указывается файловая переменная, в остальном работа совпадает со стандартными scanf и printf .
Пример. Файл text.txt в текущей папке приложения имеет следующий вид:
Прочитаем его как последовательность вещественных чисел.
1. Функции семейства scanf возвращают целое число - количество значений, которые успешно прочитаны в соответствии с указанным форматом. В реальных приложениях эту величину следует проверять в коде:
2. На "восприятие" программой данных может влиять установленная в приложении локаль. Например, если до показанного кода выполнен оператор результат работы кода может измениться (для русской локали разделителем целой и дробной части числа является запятая, а не точка).
3. Очередное чтение данных изменяет внутренний файловый указатель. Этот указатель в любой момент времени, пока файл открыт, показывает на следующее значение, которое будет прочитано. Благодаря этому наш код с "бесконечным" while не зациклился.
4. Код показывает, как читать из файла заранее неизвестное количество значений – это позволяет сделать стандартная функция feof (проверка, достигнут ли конец файла; вернёт не 0, если прочитано всё).
5. Распространённый в примерах из Сети код вида
в ряде компиляторов может породить неточности при интерпретации данных. Например, этот код может прочитать как последнее значение завершающий перевод строки в файле, благодаря чему последнее прочитанное значение "удвоится".
В качестве примера форматной записи в файл сохраним массив a из 10 целочисленных значений в файле с именем result.txt по 5 элементов в строке:
Важно! Ввод/вывод функциями библиотеки stdio.h буферизован, то есть, данные "пропускаются" через область памяти заданного размера, обмен данными происходит не отдельными байтами, а "порциями". Поэтому перед чтением данных желательно очищать буфер от возможных "остатков" предыдущего чтения методом fflush , а после записи данных следует обязательно закрывать файл методом fclose , иначе данные могут быть потеряны. Заметим, что консольный ввод/вывод "обычными" методами scanf и printf также буферизован.
- fgetc и fputc - для посимвольного чтения и посимвольной записи данных;
- fgets и fputs - для чтения и записи строк с указанным максимальным размером.
Как и в случае с функциями для чтения форматированных данных, у всех этих методов имеются аналоги для работы со стандартным вводом/выводом.
Пример. Читая файл, определить длину каждой строки в символах. Для решения задачи воспользуемся тем фактом, что строки завершаются символом "перевод строки" ( '\n' ). Предполагается, что файл уже открыт для чтения.
Важно! Из-за особенностей реализации fgetc , без последней проверки за телом цикла код мог "не обратить внимания", например, на последнюю строку файла, состоящую только из пробелов и не завершающуюся переводом строки.
Пример. Читаем построчно файл с известной максимальной длиной строки. Предполагается, что файл уже открыт для чтения.
Важно! Без дополнительной обработки прочитанные из файла строки при выводе будут содержать лишние пустые строки между строками данных. Это происходит потому, что функция fgets читает строку файла вместе с символом перевода строки (точней, под Windows - с парой символов \r\n , интерпретируемых как один), а puts добавляет к выводимой строке ещё один перевод строки.
- void *buffer - нетипизированный указатель на место хранения данных;
- size_t (unsigned) size - размер элемента данных в байтах.
- size_t count - максимальное количество элементов, которые требуется прочитать (записать);
- FILE *stream - указатель на структуру FILE
Пример. Целочисленный массив a запишем в двоичный файл.
Учитывая, что данные массива хранятся в последовательно идущих адресах памяти, цикл for для записи мы могли заменить одним оператором:
Подход к чтению данных с помощью fread аналогичен. Например, если файл уже открыт для чтения в режиме "rb":
- функции fgetpos и ftell позволяют выполнить чтение текущей позиции указателя в файле;
- функции fseek и fsetpos позволяют осуществить переход к нужной позиции в файле.
Пример. Определить размер файла в байтах, предположим, что файл уже открыт в режиме чтения или произвольного доступа.
Материал для чтения из пособия: пп. 8.6-8.11. Обратите внимание на таблицы с описанными прототипами функций ввода/вывода.
Рекомендуемые задачи: базовое задание включает две задачи, первая из которых предполагает обработку файла как текстовых данных, вторая – как бинарных. В качестве дополнительной третьей задачи может быть предложена реализация одной из задач 1, 2, содержащая консольный интерфейс и меню.
Стандартная библиотека Си предоставляет набор функций для работы с текстами. К сожалению, большая часть из них ориентирована на представление символов в виде одного байта (во время разработки языка Си кодировка Unicode, в которой на символ отводится два байта, еще не существовала). Функции можно разделить на две группы:
- функции, определяющие тип символа, - является ли он буквой, цифрой, пробелом, знаком препинания и т.п. Эти функции описаны в стандартном заголовочном файле "ctype.h". Увы, функции, касающиеся букв, работают только для латинского алфавита;
- функции для работы с текстовыми строками. Строкой в Си считается последовательность байтов, ограниченная в конце нулевым байтом. Функции работы со строками описаны в стандартном заголовочном файле "string.h".
Определение типов символов
Библиотека Си предоставляет следующие функции для определения типа символов, описанные в стандартном заголовочном файле "ctype.h":
Функции, начинающиеся с префикса is , возвращают ненулевое значение (т.е. истину), если символ с кодом c принадлежит указанному классу, и нулевое значение (ложь) в противном случае. Функции toupper и tolower преобразуют латинские буквы к верхнему или нижнему регистру, на остальных символах они действуют тождественно.
В качестве примера использования функции isspace модифицируем программу "wc.cpp", подсчитывающую число строк и символов в текстовом файле. Добавим в нее подсчет слов. Будем считать словами любые связные группы символов, разделенные пробелами, табуляциями или разделителями строк.
Пример выполнения программы wc2 в применении к собственному тексту, записанному в файле "wc2.cpp":
Работа с текстовыми строками
Стандартная библиотека Си предоставляет средства вычисления длины строки, копирования, сравнения, соединения (конкатенации) строк, поиска вхождений одной строки в другую. Функции описаны в стандартном заголовочном файле "string.h". Прототипы наиболее часто используемых функций приведены ниже.
Определение длины строки
size_t strlen(const char *s); длина строки.
Копирование строк
char *strcpy(char * dst , const char *src); копировать строку src в строку dst ;
char * strncpy (char * dst , const char *src, size_t maxlen); копировать строку src в dst , не более maxlen символов;
char * strcat (char * dst , const char *src); копировать строку src в конец dst (конкатенация строк).
Работа с произвольными массивами байтов
void *memmove(void * dst , const void *src, size_t len); копировать область памяти с адресом src размером len байтов в область памяти с адресом dst ;
void *memset(void * dst , int value, size_t len); записать значение value в каждый из len байтов, начиная с адреса dst .
Сравнение строк
int strcmp (const char *s1, const char *s2); лексикографическое сравнение строк s1 и s2 . Результат нулевой, если строки равны, отрицательный, если первая строка меньше второй, и положительный, если первая строка больше второй;
int strncmp (const char *s1, const char *s2, size_t maxlen); сравнение строк s1 и s2 , сравнивается не более maxlen символов;
int memcmp(const void *m1, const void *m2, size_t len); сравнение областей памяти с адресами m1 и m2 размером len каждая.
Поиск
char * strchr (const char *s, int c); найти первое вхождение символа c в строку s . Функция возвращает указатель на найденный символ или ноль в случае неудачи;
char * strstr (const char *s1, const char *s2); найти первое вхождение строки s2 в строку s1 . Функция возвращает указатель на найденную подстроку в s1 , равную строке s2 , или ноль в случае неудачи.
Пример: программа "Записная книжка"
В качестве примера работы с текстовой информацией рассмотрим программу "Записная книжка". Записная книжка хранит имена людей и их телефоны. Под именем понимается полное имя, включающее фамилию, имя, отчество в произвольном формате: имя представляется любой текстовой строкой. Телефон также представляется текстовой строкой (представление с помощью целого числа не подходит, потому что номер может быть очень длинным и содержать специальные символы, например, знак плюс).
Программа позволяет добавлять записи в записную книжку, удалять записи и искать номер телефона по имени. При поиске не обязательно вводить имя полностью, достаточно нескольких первых символов. Можно также распечатать содержимое всей записной книжки. В перерывах между запусками программы информация сохраняется в файле " NoteBook .dat".
Записная книжка соответствует структуре данных нагруженное множество, которая будет рассмотрена в разделе 4.6.1. и для которой имеется ряд реализаций, обеспечивающих быстрый поиск и модификацию. Мы, однако, ограничимся сейчас простейшей реализацией: не упорядочиваем записи по алфавиту и применяем последовательный поиск . Записи хранятся в массиве, его максимальный размер равен 1000 элементов. Новая запись добавляется в конец массива. При удалении записи из книжки последняя запись переписывается на место удаленной.
Для записей используется структурный тип NameRecord , определенный следующим образом:
Здесь поле name структуры является указателем на имя абонента, которое представляет собой текстовую строку. Номер телефона также задается строкой, указатель на которую записывается в поле phone . Пространство под строки захватывается в динамической памяти с помощью функции malloc . При удалении записи память освобождается с помощью функции free .
Записи хранятся в массиве records :
Константа MAXNAMES задает максимально возможный размер массива records . Текущее количество записей (т.е. реальный размер массива) хранится в переменной numRecords .
В начале работы программа вводит содержимое записной книжки из файла " NoteBook .dat, имя которого задается как константный указатель на константную строку:
В конце работы содержимое записной книжки сохраняется в файле. Для ввода используется функция loadNoteBook , для сохранения - функция saveNoteBook с прототипами
Каждой записи соответствует пара строк файла. Пусть, например, имя абонента "Иван Петров", телефон - "123-45-67". Этой записи соответствует пара строк
Записи в файле разделяются пустыми строками.
Сохранение файла выполняется только в случае, когда содержимое записной книжки изменялось в процессе работы. За это отвечает переменная
которая принимает значение true , если хотя бы раз была выполнена одна из команд, меняющих содержимое записной книжки.
Работа с записной книжкой происходит в интерактивном режиме. Пользователь вводит команду, программа ее выполняет. При выполнении команды программа просит ввести дополнительную информацию, если это необходимо, и печатает результат выполнения. Команды записываются латинскими буквами, чтобы исключить возможные проблемы с русскими кодировками. После команды может идти имя абонента. Реализованы следующие команды:
Каждой команде соответствует отдельная функция, выполняющая команду. Например, команде add соответствует функция onAdd , команде find - функция onFind и т.д. Начало искомого имени и его длина, если имя указано в командной строке, передаются через статические переменные
Если имя не указано в командной строке, то для его ввода вызывается функция readName , которая просит пользователя ввести имя с клавиатуры, считывает имя и заполняет переменные namePrefix и namePrefixLen .
Для поиска записи с заданным началом имени используется функция search с прототипом
Ей передается начало искомого имени и индекс элемента массива, с которого начинается поиск. Второй аргумент нужен для того, чтобы последовательно находить имена с одинаковым префиксом (например, имена, начинающиеся с заданной буквы). Функция возвращает индекс найденного элемента в случае успеха или отрицательное значение -1 при неудаче. Применяется последовательный поиск , при котором просматриваются все элементы, начиная со стартовой позиции. Для сравнения имен используется стандартная функция strncmp (s1, s2, n) , которая сравнивает n первых символов строк s1 и s2 . При поиске в качестве s1 используется заданное начало имени, в качестве s2 -- очередное имя из записной книжке, n равно длине начала имени.
Для ввода строки с клавиатуры мы используем вспомогательную функцию readLine с прототипом
Функция вводит строку из стандартного входного потока, используя библиотечную функцию fgets . Затем из конца строки удаляются символы-разделители строк " \r " и " \n ". Введенная строка помещается в массив buffer с максимальной длиной maxlen . Реальная длина введенной строки записывается в переменную, адрес которой передается через указатель len .
Полный текст программы:
Аргументы командной строки
До сих пор во всех примерах программ использовался ввод исходных данных либо с клавиатуры (т.е. из входного потока), либо из файла. Язык Си предоставляет также возможность указывать аргументы программы в командной строке.
Аргументы командной строки являются параметрами функции main , с которой начинается выполнение Си-программы. До сих пор применялся вариант функции main без параметров, однако, при необходимости доступа к аргументам командной строки можно использовать следующий заголовок функции main :
Здесь целая переменная argc равна числу аргументов, т.е. отдельных слов командной строки, а массив argv содержит указатели на строки, каждая из которых равна очередному слову командной строки. Нулевой элемент argv[0] равен имени программы. Таким образом, число аргументов argc всегда не меньше единицы.
Например, при запуске программы testprog с помощью командной строки
значение переменной argc будет равно 4, а массив argv будет содержать 4 строки " testprog ", " -x ", " abcd.txt " и " efgh.txt ".
В операционной системе Unix нулевой элемент массива argv содержит полный путь к файлу с выполняемой программой. В системах MS DOS и MS Windows строка argv[0] может быть равна как полному пути к файлу, так и первому слову командной строки (зависит от используемого командного процессора).
Пример программы, печатающей аргументы своей командной строки:
Разработка больших проектов
До сих пор все рассмотренные примеры программ на Си имели небольшой объем (за исключением, возможно, программы Записная книжка). Такие маленькие программы помещаются в один файл . Однако реальные проекты имеют, как правило, значительно больший объем, измеряемый десятками, а чаще сотнями тысяч строк. Реализовать такую программу в виде одного непрерывного текста, помещающегося в одном файле, невозможно. Большой проект разбивается на более или менее независимые модули, которые можно написать и отладить по отдельности. Обычно в один такой модуль выделяется группа функций, работающих над общими глобальными данными и в той или иной мере связанных логически между собой. В простейшем случае каждому модулю соответствуют два файла с исходными текстами: заголовочный, или h - файл , описывающий интерфейс модуля, и файл реализации - c - или cpp - файл . Заголовочный файл содержит прототипы функций модуля, описания констант и глобальных переменных, структур, определения используемых типов и т.п. Файл реализации содержит определения глобальных переменных (т.е. их описания без слова extern ), определения статических переменных, которые не экспортируются за пределы данного файла (т.е. описания со словом static ), реализацию глобальных функций, а также описания прототипов и реализацию вспомогательных функций, которые не экспортируются за пределы файла.
Технология тестирования и отладки отдельного модуля предполагает использование заглушек вместо функций из других модулей, вызываемых функциями данного модуля, в том случае, когда другие модули еще не реализованы.
Существуют различные подходы к разбиению проекта на модули. Наиболее популярна технология сверху вниз, когда основной модуль реализуется на первом шаге на основе нескольких вспомогательных, интерфейс которых формулируется по мере реализации основного модуля. На следующем шаге реализуются вспомогательные модули, для этого придумываются новые вспомогательные модули более низкого уровня и т.д., пока не дойдем до базовых исполнителей.
Отметим, что, даже не используя объектно-ориентированного языка, можно придерживаться объектно-ориентированного стиля программирования, т.е. выделять группы функций и общие глобальные или статические данные, над которыми эти функции работают. Такие группы, подобно классам, реализуются и отлаживаются как отдельные структурные единицы.
Пример небольшого проекта " Стековый калькулятор " будет рассмотрен в следующей главе, посвященной структурам данных.
Читайте также: