Как считать числа из файла c
Я прикинул предварительный план решения и составил 3 пункта:
1) (То, что я незнаю как реализовать.): Читаем *.txt файл chisla и выделяем из этого файла все вещественные числа стоящие
через пробел (пример файла : "234432.324422 565463.4353 2425654.478".. и т.д.) Записываем по порядку каждое число в новый
динамический массив chisla, размер массива = числу чисел из файла.
2) Находим среднее значение всех чисел используя массив, это я могу сделать.
3) Выводим на экран все элементы массива, меньшие чем их среднее значение. Это я тоже могу реализовать.
Заранее спасибо за любую помощь.
1) (То, что я незнаю как реализовать.): Читаем *.txt файл chisla и выделяем из этого файла все вещественные числа стоящие
через пробел (пример файла : "234432.324422 565463.4353 2425654.478".. и т.д.) Записываем по порядку каждое число в новый
динамический массив chisla, размер массива = числу чисел из файла.
.
Это ты с числами решил общаться как со строками? Почему не с бинарным файлом, а именно с текстровым?
Ммм. если я что-то говорю не так, то прошу прощения.Опыта работы с файлами в си ранее не имел. да не, все так, это твоя мысля и твое право работать с числами и файлами так, как захочется. Просто удобнее было бы на мой взгляд забить в бинарный файл числа типа long long, чтобы на все хватило, и затем читать их. Иначе тебе придется парсить строку, выковыривая числа, и затем юзать нечто вроде strtoint. На мой взгляд, если задача не критична, лучше юзать блочные чтение/запись бинарного файла. Вот небольшой примерчик
int main( void )
int count
long long array1[SIZE], array2[SIZE];
FILE *fp;
// забиваем массив 1 числами 0,1,2,4. 38
for (count = 0; count < SIZE; count++)
array1[count] = (long long) (2 * count);
// открываем бинарный файл
if ( (fp = fopen("file.bin", "wb")) == NULL)
fprintf(stderr, "Error opening file.");
exit(1);
>
// сохраняем в файл первый массив
// fwrite (void *buff, int size, int count, FILE *fp)
// buff - указатель на память, которую пишем
// size - размер отдельных элементов
// count - число элементов
if (fwrite(array1, sizeof(long long), SIZE, fp) != SIZE)
fprintf(stderr, "Error writing to file.");
exit(1);
>
// Открываем файл на чтение
if ( (fp = fopen("file.bin", "rb")) == NULL)
fprintf(stderr, "Error opening file.");
exit(1);
>
// Читаем файл в массив 2
if (fread(array2, sizeof(long long), SIZE, fp) != SIZE)
fprintf(stderr, "Error reading file.");
exit(1);
>
// Во втором массиве должны быть полученные данные, выводи их, сверяй и т.д.
. Просто удобнее было бы на мой взгляд забить в бинарный файл числа типа long long, чтобы на все хватило, и затем читать их. Иначе тебе придется парсить строку, выковыривая числа, и затем юзать нечто вроде strtoint. На мой взгляд, если задача не критична, лучше юзать блочные чтение/запись бинарного файла.
Зачем создавать бинарный файл,когда по условию файл дан ? В нем находятся какие то числа.Массив стоит сделать 2-мерный(или числа в файле идут в одну строку ? Вряд ли).Ничего не надо выковыривать,никакого stroint( ? ).Все проще.Я делаю единственное допущение,что в первой строке файла записан размер массива и мы его считываем(облом считать:) ).Далее читаем весь массив.
До этого при вводе-выводе данных мы работали со стандартными потоками — клавиатурой и монитором. Теперь рассмотрим, как в языке C реализовано получение данных из файлов и запись их туда. Перед тем как выполнять эти операции, надо открыть файл и получить доступ к нему.
В языке программирования C указатель на файл имеет тип FILE и его объявление выглядит так:
С другой стороны, функция fopen() открывает файл по указанному в качестве первого аргумента адресу в режиме чтения ("r"), записи ("w") или добавления ("a") и возвращает в программу указатель на него. Поэтому процесс открытия файла и подключения его к программе выглядит примерно так:
Примечание. В случае использования относительной адресации текущим/рабочим каталогом в момент исполнения программы должен быть тот, относительно которого указанный относительный адрес корректен. Место нахождения самого исполняемого файла не важно.
При чтении или записи данных в файл обращение к нему осуществляется посредством файлового указателя (в данном случае, myfile).
Если в силу тех или иных причин (нет файла по указанному адресу, запрещен доступ к нему) функция fopen() не может открыть файл, то она возвращает NULL. В реальных программах почти всегда обрабатывают ошибку открытия файла в ветке if , мы же далее опустим это.
Объявление функции fopen() содержится в заголовочном файле stdio.h, поэтому требуется его подключение. Также в stdio.h объявлен тип-структура FILE.
После того, как работа с файлом закончена, принято его закрывать, чтобы освободить буфер от данных и по другим причинам. Это особенно важно, если после работы с файлом программа продолжает выполняться. Разрыв связи между внешним файлом и указателем на него из программы выполняется с помощью функции fclose() . В качестве параметра ей передается указатель на файл:
В программе может быть открыт не один файл. В таком случае каждый файл должен быть связан со своим файловым указателем. Однако если программа сначала работает с одним файлом, потом закрывает его, то указатель можно использовать для открытия второго файла.
Чтение из текстового файла и запись в него
fscanf()
Функция fscanf() аналогична по смыслу функции scanf() , но в отличии от нее осуществляет форматированный ввод из файла, а не стандартного потока ввода. Функция fscanf() принимает параметры: файловый указатель, строку формата, адреса областей памяти для записи данных:
Возвращает количество удачно считанных данных или EOF. Пробелы, символы перехода на новую строку учитываются как разделители данных.
Допустим, у нас есть файл содержащий такое описание объектов:
Тогда, чтобы считать эти данные, мы можем написать такую программу:
В данном случае объявляется структура и массив структур. Каждая строка из файла соответствует одному элементу массива; элемент массива представляет собой структуру, содержащую строковое и два числовых поля. За одну итерацию цикл считывает одну строку. Когда встречается конец файла fscanf() возвращает значение EOF и цикл завершается.
fgets()
Функция fgets() аналогична функции gets() и осуществляет построчный ввод из файла. Один вызов fgets() позволят прочитать одну строку. При этом можно прочитать не всю строку, а лишь ее часть от начала. Параметры fgets() выглядят таким образом:
Такой вызов функции прочитает из файла, связанного с указателем myfile, одну строку текста полностью, если ее длина меньше 50 символов с учетом символа '\n', который функция также сохранит в массиве. Последним (50-ым) элементом массива str будет символ '\0', добавленный fgets() . Если строка окажется длиннее, то функция прочитает 49 символов и в конце запишет '\0'. В таком случае '\n' в считанной строке содержаться не будет.
В этой программе в отличие от предыдущей данные считываются строка за строкой в массив arr. Когда считывается следующая строка, предыдущая теряется. Функция fgets() возвращает NULL в случае, если не может прочитать следующую строку.
getc() или fgetc()
Функция getc() или fgetc() (работает и то и другое) позволяет получить из файла очередной один символ.
Приведенный в качестве примера код выводит данные из файла на экран.
Запись в текстовый файл
Также как и ввод, вывод в файл может быть различным.
- Форматированный вывод. Функция fprintf ( файловый_указатель, строка_формата, переменные ) .
- Посточный вывод. Функция fputs ( строка, файловый_указатель ) .
- Посимвольный вывод. Функция fputc() или putc( символ, файловый_указатель ) .
Ниже приводятся примеры кода, в которых используются три способа вывода данных в файл.
Запись в каждую строку файла полей одной структуры:
Построчный вывод в файл ( fputs() , в отличие от puts() сама не помещает в конце строки '\n'):
Пример посимвольного вывода:
Чтение из двоичного файла и запись в него
С файлом можно работать не как с последовательностью символов, а как с последовательностью байтов. В принципе, с нетекстовыми файлами работать по-другому не возможно. Однако так можно читать и писать и в текстовые файлы. Преимущество такого способа доступа к файлу заключается в скорости чтения-записи: за одно обращение можно считать/записать существенный блок информации.
При открытии файла для двоичного доступа, вторым параметром функции fopen() является строка "rb" или "wb".
Тема о работе с двоичными файлами достаточно сложная, для ее изучения требуется отдельный урок. Здесь будут отмечены только особенности функций чтения-записи в файл, который рассматривается как поток байтов.
Функции fread() и fwrite() принимают в качестве параметров:
- адрес области памяти, куда данные записываются или откуда считываются,
- размер одного данного какого-либо типа,
- количество считываемых данных указанного размера,
- файловый указатель.
Эти функции возвращают количество успешно прочитанных или записанных данных. Т.е. можно "заказать" считывание 50 элементов данных, а получить только 10. Ошибки при этом не возникнет.
Пример использования функций fread() и fwrite() :
Здесь осуществляется попытка чтения из первого файла 50-ти символов. В n сохраняется количество реально считанных символов. Значение n может быть равно 50 или меньше. Данные помещаются в строку. То же самое происходит со вторым файлом. Далее первая строка присоединяется ко второй, и данные сбрасываются в третий файл.
Для удобства обращения информация в запоминающих устройствах хранится в виде файлов.
Файл – именованная область внешней памяти, выделенная для хранения массива данных. Данные, содержащиеся в файлах, имеют самый разнообразный характер: программы на алгоритмическом или машинном языке; исходные данные для работы программ или результаты выполнения программ; произвольные тексты; графические изображения и т. п.
Каталог ( папка , директория ) – именованная совокупность байтов на носителе информации, содержащая название подкаталогов и файлов, используется в файловой системе для упрощения организации файлов.
Файловой системой называется функциональная часть операционной системы, обеспечивающая выполнение операций над файлами. Примерами файловых систем являются FAT (FAT – File Allocation Table, таблица размещения файлов), NTFS, UDF (используется на компакт-дисках).
Существуют три основные версии FAT: FAT12, FAT16 и FAT32. Они отличаются разрядностью записей в дисковой структуре, т.е. количеством бит, отведённых для хранения номера кластера. FAT12 применяется в основном для дискет (до 4 кбайт), FAT16 – для дисков малого объёма, FAT32 – для FLASH-накопителей большой емкости (до 32 Гбайт).
Рассмотрим структуру файловой системы на примере FAT32.
Файловая структура FAT32
Устройства внешней памяти в системе FAT32 имеют не байтовую, а блочную адресацию. Запись информации в устройство внешней памяти осуществляется блоками или секторами.
Сектор – минимальная адресуемая единица хранения информации на внешних запоминающих устройствах. Как правило, размер сектора фиксирован и составляет 512 байт. Для увеличения адресного пространства устройств внешней памяти сектора объединяют в группы, называемые кластерами.
Кластер – объединение нескольких секторов, которое может рассматриваться как самостоятельная единица, обладающая определёнными свойствами. Основным свойством кластера является его размер, измеряемый в количестве секторов или количестве байт.
Файловая система FAT32 имеет следующую структуру.
Нумерация кластеров, используемых для записи файлов, ведется с 2. Как правило, кластер №2 используется корневым каталогом, а начиная с кластера №3 хранится массив данных. Сектора, используемые для хранения информации, представленной выше корневого каталога, в кластеры не объединяются.
Минимальный размер файла, занимаемый на диске, соответствует 1 кластеру.
Загрузочный сектор начинается следующей информацией:
- EB 58 90 – безусловный переход и сигнатура;
- 4D 53 44 4F 53 35 2E 30 MSDOS5.0;
- 00 02 – количество байт в секторе (обычно 512);
- 1 байт – количество секторов в кластере;
- 2 байта – количество резервных секторов.
Кроме того, загрузочный сектор содержит следующую важную информацию:
- 0x10 (1 байт) – количество таблиц FAT (обычно 2);
- 0x20 (4 байта) – количество секторов на диске;
- 0x2С (4 байта) – номер кластера корневого каталога;
- 0x47 (11 байт) – метка тома;
- 0x1FE (2 байта) – сигнатура загрузочного сектора ( 55 AA ).
Сектор информации файловой системы содержит:
- 0x00 (4 байта) – сигнатура ( 52 52 61 41 );
- 0x1E4 (4 байта) – сигнатура ( 72 72 41 61 );
- 0x1E8 (4 байта) – количество свободных кластеров, -1 если не известно;
- 0x1EС (4 байта) – номер последнего записанного кластера;
- 0x1FE (2 байта) – сигнатура ( 55 AA ).
Таблица FAT содержит информацию о состоянии каждого кластера на диске. Младшие 2 байт таблицы FAT хранят F8 FF FF 0F FF FF FF FF (что соответствует состоянию кластеров 0 и 1, физически отсутствующих). Далее состояние каждого кластера содержит номер кластера, в котором продолжается текущий файл или следующую информацию:
- 00 00 00 00 – кластер свободен;
- FF FF FF 0F – конец текущего файла.
Корневой каталог содержит набор 32-битных записей информации о каждом файле, содержащих следующую информацию:
- 8 байт – имя файла;
- 3 байта – расширение файла;
Корневой каталог содержит набор 32-битных записей информации о каждом файле, содержащих следующую информацию:
- 8 байт – имя файла;
- 3 байта – расширение файла;
- 1 байт – атрибут файла:
- 1 байт – зарезервирован;
- 1 байт – время создания (миллисекунды) (число от 0 до 199);
- 2 байта – время создания (с точностью до 2с):
- 2 байта – дата создания:
- 2 байта – дата последнего доступа;
- 2 байта – старшие 2 байта начального кластера;
- 2 байта – время последней модификации;
- 2 байта – дата последней модификации;
- 2 байта – младшие 2 байта начального кластера;
- 4 байта – размер файла (в байтах).
В случае работы с длинными именами файлов (включая русские имена) кодировка имени файла производится в системе кодировки UTF-16. При этого для кодирования каждого символа отводится 2 байта. При этом имя файла записывается в виде следующей структуры:
- 1 байт последовательности;
- 10 байт содержат младшие 5 символов имени файла;
- 1 байт атрибут;
- 1 байт резервный;
- 1 байт – контрольная сумма имени DOS;
- 12 байт содержат младшие 3 символа имени файла;
- 2 байта – номер первого кластера;
- остальные символы длинного имени.
Далее следует запись, включающая имя файла в формате 8.3 в обычном формате.
Работа с файлами в языке Си
Для программиста открытый файл представляется как последовательность считываемых или записываемых данных. При открытии файла с ним связывается поток ввода-вывода . Выводимая информация записывается в поток, вводимая информация считывается из потока.
Когда поток открывается для ввода-вывода, он связывается со стандартной структурой типа FILE , которая определена в stdio.h . Структура FILE содержит необходимую информацию о файле.
Открытие файла осуществляется с помощью функции fopen() , которая возвращает указатель на структуру типа FILE , который можно использовать для последующих операций с файлом.
- "r" — открыть файл для чтения (файл должен существовать);
- "w" — открыть пустой файл для записи; если файл существует, то его содержимое теряется;
- "a" — открыть файл для записи в конец (для добавления); файл создается, если он не существует;
- "r+" — открыть файл для чтения и записи (файл должен существовать);
- "w+" — открыть пустой файл для чтения и записи; если файл существует, то его содержимое теряется;
- "a+" — открыть файл для чтения и дополнения, если файл не существует, то он создаётся.
Функция fclose() закрывает поток или потоки, связанные с открытыми при помощи функции fopen() файлами. Закрываемый поток определяется аргументом функции fclose() .
Возвращаемое значение: значение 0, если поток успешно закрыт; константа EOF , если произошла ошибка.
Чтение символа из файла:
Аргументом функции является указатель на поток типа FILE . Функция возвращает код считанного символа. Если достигнут конец файла или возникла ошибка, возвращается константа EOF .
Запись символа в файл:
Аргументами функции являются символ и указатель на поток типа FILE . Функция возвращает код считанного символа.
Функции fscanf() и fprintf() аналогичны функциям scanf() и printf() , но работают с файлами данных, и имеют первый аргумент — указатель на файл.
Функции fgets() и fputs() предназначены для ввода-вывода строк, они являются аналогами функций gets() и puts() для работы с файлами.
Копирует строку в поток с текущей позиции. Завершающий нуль- символ не копируется.
Пример Ввести число и сохранить его в файле s1.txt. Считать число из файла s1.txt, увеличить его на 3 и сохранить в файле s2.txt.
Файлы позволяют пользователю считывать большие объемы данных непосредственно с диска, не вводя их с клавиатуры. Существуют два основных типа файлов: текстовые и двоичные.
Текстовыми называются файлы, состоящие из любых символов. Они организуются по строкам, каждая из которых заканчивается символом «конца строки». Конец самого файла обозначается символом «конца файла». При записи информации в текстовый файл, просмотреть который можно с помощью любого текстового редактора, все данные преобразуются к символьному типу и хранятся в символьном виде.
В двоичных файлах информация считывается и записывается в виде блоков определенного размера, в которых могут храниться данные любого вида и структуры.
Для работы с файлами используются специальные типы данных, называемые потоками. Поток ifstream служит для работы с файлами в режиме чтения, а ofstream в режиме записи. Для работы с файлами в режиме как записи, так и чтения служит поток fstream.
В программах на C++ при работе с текстовыми файлами необходимо подключать библиотеки iostream и fstream.
Для того чтобы записывать данные в текстовый файл, необходимо:
- описать переменную типа ofstream.
- открыть файл с помощью функции open.
- вывести информацию в файл.
- обязательно закрыть файл.
Для считывания данных из текстового файла, необходимо:
- описать переменную типа ifstream.
- открыть файл с помощью функции open.
- считать информацию из файла, при считывании каждой порции данных необходимо проверять, достигнут ли конец файла.
- закрыть файл.
Запись информации в текстовый файл
Как было сказано ранее, для того чтобы начать работать с текстовым файлом, необходимо описать переменную типа ofstream. Например, так:
ofstream F;
Будет создана переменная F для записи информации в файл. На следующим этапе файл необходимо открыть для записи. В общем случае оператор открытия потока будет иметь вид:
F.open(«file», mode);
Здесь F — переменная, описанная как ofstream, file — полное имя файла на диске, mode — режим работы с открываемым файлом. Обратите внимание на то, что при указании полного имени файла нужно ставить двойной слеш. Для обращения, например к файлу accounts.txt, находящемуся в папке sites на диске D, в программе необходимо указать: D:\\sites\\accounts.txt.
Файл может быть открыт в одном из следующих режимов:
- ios::in — открыть файл в режиме чтения данных; режим является режимом по умолчанию для потоков ifstream;
- ios::out — открыть файл в режиме записи данных (при этом информация о существующем файле уничтожается); режим является режимом по умолчанию для потоков ofstream;
- ios::app — открыть файл в режиме записи данных в конец файла;
- ios::ate — передвинуться в конец уже открытого файла;
- ios::trunc — очистить файл, это же происходит в режиме ios::out;
- ios::nocreate — не выполнять операцию открытия файла, если он не существует;
- ios::noreplace — не открывать существующий файл.
Параметр mode может отсутствовать, в этом случае файл открывается в режиме по умолчанию для данного потока.
После удачного открытия файла (в любом режиме) в переменной F будет храниться true, в противном случае false. Это позволит проверить корректность операции открытия файла.
Открыть файл (в качестве примера возьмем файл D:\\sites\\accounts.txt) в режиме записи можно одним из следующих способов:
После открытия файла в режиме записи будет создан пустой файл, в который можно будет записывать информацию.
Если вы хотите открыть существующий файл в режиме дозаписи, то в качестве режима следует использовать значение ios::app.
После открытия файла в режиме записи, в него можно писать точно так же, как и на экран, только вместо стандартного устройства вывода cout необходимо указать имя открытого файла.
Например, для записи в поток F переменной a, оператор вывода будет иметь вид:
Для последовательного вывода в поток G переменных b, c, d оператор вывода станет таким:
Закрытие потока осуществляется с помощью оператора:
F.close();
В качестве примера рассмотрим следующую задачу.
Задача 1
Создать текстовый файл D:\\sites\\accounts.txt и записать в него n вещественных чисел.
Решение
12
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Чтение информации из текстового файла
Для того чтобы прочитать информацию из текстового файла, необходимо описать переменную типа ifstream. После этого нужно открыть файл для чтения с помощью оператора open. Если переменную назвать F, то первые два оператора будут такими:
После открытия файла в режиме чтения из него можно считывать информацию точно так же, как и с клавиатуры, только вместо cin нужно указать имя потока, из которого будет происходить чтение данных.
Например, для чтения данных из потока F в переменную a, оператор ввода будет выглядеть так:
Два числа в текстовом редакторе считаются разделенными, если между ними есть хотя бы один из символов: пробел, табуляция, символ конца строки. Хорошо, когда программисту заранее известно, сколько и какие значения хранятся в текстовом файле. Однако часто известен лишь тип значений, хранящихся в файле, при этом их количество может быть различным. Для решения данной проблемы необходимо считывать значения из файла поочередно, а перед каждым считыванием проверять, достигнут ли конец файла. А поможет сделать это функция F.eof(). Здесь F — имя потока функция возвращает логическое значение: true или false, в зависимости от того достигнут ли конец файла.
Следовательно, цикл для чтения содержимого всего файла можно записать так:
//организуем для чтения значений из файла, выполнение//цикла прервется, когда достигнем конец файла,
//в этом случае F.eof() вернет истину
while ( ! F. eof ( ) )
<
//чтение очередного значения из потока F в переменную a
F >> a ;
//далее идет обработка значения переменной a
>
Для лучшего усвоения материала рассмотрим задачу.
Задача 2
В текстовом файле D:\\game\\accounts.txt хранятся вещественные числа, вывести их на экран и вычислить их количество.
Решение
12
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
На этом относительно объемный урок по текстовым файлам закончен. В следующей статье будут рассмотрены методы манипуляции, при помощи которых в C++ обрабатываются двоичные файлы.
Читайте также: