Как записать float в файл с
Текстовыми называют файлы, состоящие из любых символов. Они организуются по строкам, каждая из которых заканчивается символом "конец строки". Конец самого файла обозначается символом " конец файла ". При записи информации в текстовый файл , просмотреть который можно с помощью любого текстового редактора, все данные преобразуются к символьному типу и хранятся в символьном виде.
Для работы с файлами используются специальные типы данных, называемые потоками 4 Вообще говоря, потоки являются классами, которым будет посвящена специальная глава. . Поток ifstream служит для работы с файлами в режиме чтения. Поток ofstream служит для работы с файлами в режиме записи. Для работы с файлами в режиме как чтения, так и записи служит поток iofstream .
В программах на C++ при работе с текстовыми файлами необходимо подключать библиотеки iostream и fstream .
Для того, чтобы записать данные в текстовый файл , необходимо:
- Описать переменную типа ofstream.
- Открыть файл с помощью функции open.
- Вывести информацию в файл.
- Закрыть файл.
Для того, чтобы считать данные из текстового файла, необходимо:
- Описать переменную типа ifstream.
- Открыть файл с помощью функции open.
- Считать информацию из файла, при считывании каждой порции данных необходимо проверять достигнут ли конец файла.
- Закрыть файл.
7.2.1 Запись информации в текстовый файл
Для того, чтобы начать работать с текстовым файлом, необходимо описать переменную типа ofstream . Например, с помощью оператора
ofstream F; 5 Далее термин "поток" будет использоваться для указания переменной, описанной как ofstream, ifstream, iofstream , а термин "файл" для указания реального файла на диске.
будет создана переменная F для записи информации в файл. На следующем этапе файл необходимо открыть для записи. В общем случае оператор открытия потока будет иметь вид:
Здесь F — переменная, описанная как ofstream, file — имя файла на диске, mode — режим работы с открываемым файлом.
Файл может быть открыт в одном из следующих режимов:
- ios::in — открыть файл в режиме чтения данных, этот режим является режимом по умолчанию для потоков ifstream;
- ios::out — открыть файл в режиме записи данных (при этом информация в существующем файле уничтожается), этот режим является режимом по умолчанию для потоков ofstream ;
- ios::app — открыть файл в режиме записи данных в конец файла;
- ios::ate — передвинуться в конец уже открытого файла;
- ios::trunc — очистить файл, это же происходит в режиме ios::out;
- ios::nocreate — не выполнять операцию открытия файла, если он не существует 6 При открытии файла в режиме ios::in происходит как раз обратное, если файл не существует, он создаётся ;
- ios::noreplace — не открывать существующий файл.
Параметр mode может отсутствовать, в этом случае файл открывается в режиме по умолчанию для данного потока 7 ios::in — для потоков ifstream, ios::out — для потоков ofstream . .
После удачного открытия файла (в любом режиме) в переменной F будет храниться true , в противном случае false . Это позволит проверять корректность операции открытия файла.
Открыть файл (в качестве примера возьмём файл abc.txt ) в режиме записи можно одним из следующих способов:
После открытия файла в режиме записи будет создан пустой файл, в который можно будет записывать информацию. Если нужно открыть файл, чтобы в него что-либо дописать, то в качестве режима следует использовать значение ios::app .
После открытия файла в режиме записи, в него можно писать точно также, как и на экран, только вместо стандартного устройства вывода cout необходимо указать имя открытого для записи файла.
Например, для записи в поток F переменной a оператор вывода будет иметь вид:
Для последовательного вывода в поток G переменных b, c и d оператор вывода станет таким:
Закрытие потока осуществляется с помощью оператора:
В качестве примера рассмотрим следующую задачу.
Задача 7.1. Создать текстовый файл abc.txt и записать туда n вещественных чисел.
Текст программы с комментариями:
Обратите внимание, что в текстовый файл записываются не только вещественные числа, но и символы табуляции. Таким образом, в конце файла после последнего числа находится символ табуляции. По этой причине может возникнуть проблема при чтении информации (п. 7.2.2), так как символ табуляции будет интерпретирован как вещественное число. Чтобы этого избежать в программе была применена следующая конструкция:
Здесь реализована проверка ввода последнего числа. После него символ табуляции отсутствует.
В результате работы программы будет создан текстовый файл abc.txt , который можно просмотреть средствами обычного текстового редактора (рис. 7.1,рис. 7.2).
7.2.2 Чтение информации из текстового файла
Рис. 7.1. Процесс работы программы к задаче 7.1. Ввод исходных данных.
увеличить изображение
Рис. 7.2. Текстовый файл abc.txt, созданный программой к задаче 7.1.
Для того, чтобы прочитать информацию из текстового файла, необходимо описать переменную типа ifstream . После этого файл необходимо открыть для чтения с помощью оператора open . Если переменную назвать F , то первые два оператора будут такими:
8 Указание режима ios::in можно, конечно, опустить, ведь для потока ifstream значение ios::in является значением по умолчанию, тогда оператор open можно будет записать так F.open("abc.txt");
После открытия файла в режиме чтения, из него можно считывать информацию точно так же, как и с клавиатуры, только вместо стандартного устройства ввода cin необходимо указать имя потока, из которого будет происходить чтение данных.
Например, для чтения из потока F в переменную a оператор ввода будет иметь вид:
Для последовательного ввода из потока G в переменные b, с и d оператор ввода станет таким:
Два числа в текстовом файле считаются разделёнными, если между ними есть хотя бы один из символов: пробел, табуляция, символ конца строки.
Хорошо, если программисту заранее известно, сколько и каких значений хранится в текстовом файле. Однако часто просто известен тип значений, хранящихся в файле, при этом количество значений в файле может быть различным. При решении подобной проблемы необходимо считывать значения из файла по одному, а перед каждым считыванием проверять, достигнут ли конец файла. Для проверки, достигнут или нет конец файла, служит функция
Здесь F — имя потока, функция возвращает логическое значение: true — если достигнут конец файла, если не достигнут функция возвращает значение false .
Следовательно, цикл для чтения содержимого всего файла можно записать так.
Рассмотрим следующую задачу.
Задача 7.2. В текстовом файле хранятся вещественные числа (рис. 7.2), вывести их на экран и вычислить их количество.
Текст программы с комментариями приведён ниже.
Результат работы программы к задаче 7.2:
Программа работает корректно, если текстовый файл abc.txt был создан с помощью программы к задаче 7.1. Предположим, что файл создавался в текстовом редакторе, и пользователь ввёл после последней цифры символ пробела, табуляции или перехода на новую строку. Тогда результат будет таким:
Происходит это потому, что после чтения последней цифры из потока конец файла не достигнут, оператор цикла выполняется ещё один раз, значение переменной увеличивается на единицу, а так как значение переменной не изменилось, то выводится повторно. Один из способов решения данной проблемы может быть таким:
Если количество вещественных чисел, записанных в файл, известно заранее, то текст программы можно переписать следующим образом:
Существует возможность открывать файл с данными таким образом, чтобы в него можно было дописывать информацию. Рассмотрим эту возможность на примере решения следующей задачи.
Задача 7.3. В файле (рис. 7.2) хранится массив вещественных чисел, дописать в файл этот же массив, упорядочив его по возрастанию.
Алгоритм решения задачи очень простой. Считываем в массив данные из текстового файла, упорядочиваем массив, дописываем его в этот же файл.
Для чтения данных из файла описываем поток ifstream , открываем его в режиме чтения и последовательно, пока не достигнем конца файла, считываем все элементы в массив. Сортировку проведём методом пузырька. Обратите внимание, что поток нужно открыть так, чтобы была возможность дописать в конец файла упорядоченный массив.
До этого при вводе-выводе данных мы работали со стандартными потоками — клавиатурой и монитором. Теперь рассмотрим, как в языке 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 или меньше. Данные помещаются в строку. То же самое происходит со вторым файлом. Далее первая строка присоединяется ко второй, и данные сбрасываются в третий файл.
Работа файлового ввода/вывода в языке C++ почти аналогична работе обычных потоков ввода/вывода (но с небольшими нюансами).
Классы файлового ввода/вывода
Есть три основных класса файлового ввода/вывода в языке C++:
ofstream (является дочерним классу ostream);
fstream (является дочерним классу iostream ).
С помощью этих классов можно выполнить однонаправленный файловый ввод, однонаправленный файловый вывод и двунаправленный файловый ввод/вывод. Для их использования нужно всего лишь подключить заголовочный файл fstream.
В отличие от потоков cout, cin, cerr и clog, которые сразу же можно использовать, файловые потоки должны быть явно установлены программистом. То есть, чтобы открыть файл для чтения и/или записи, нужно создать объект соответствующего класса файлового ввода/вывода, указав имя файла в качестве параметра. Затем, с помощью оператора вставки ( << ) или оператора извлечения ( >> ), можно записывать данные в файл или считывать содержимое файла. После проделывания данных действий нужно закрыть файл — явно вызвать метод close() или просто позволить файловой переменной ввода/вывода выйти из области видимости (деструктор файлового класса ввода/вывода закроет этот файл автоматически вместо нас).
Файловый вывод
Для записи в файл используется класс ofstream . Например:
// Класс ofstream используется для записи данных в файл. // Если мы не можем открыть этот файл для записи данных, cerr << "Uh oh, SomeText.txt could not be opened for writing!" << endl ; // Когда outf выйдет из области видимости, то деструктор класса ofstream автоматически закроет наш файлОбратите внимание, мы также можем использовать метод put() для записи одного символа в файл.
Файловый ввод
Теперь мы попытаемся прочитать содержимое файла, который создали в предыдущем примере. Обратите внимание, ifstream возвратит 0 , если мы достигли конца файла (это удобно для определения «длины» содержимого файла). Например:
// ifstream используется для чтения содержимого файла. // Если мы не можем открыть этот файл для чтения его содержимого, cerr << "Uh oh, SomeText.txt could not be opened for reading!" << endl ; // то перемещаем эти данные в строку, которую затем выводим на экран // Когда inf выйдет из области видимости, то деструктор класса ifstream автоматически закроет наш файлРезультат выполнения программы:
Хм, это не совсем то, что мы хотели. Как мы уже узнали на предыдущих уроках, оператор извлечения работает с «отформатированными данными», т.е. он игнорирует все пробелы, символы табуляции и символ новой строки. Чтобы прочитать всё содержимое как есть, без его разбивки на части (как в примере, приведенном выше), нам нужно использовать метод getline():
// ifstream используется для чтения содержимого файлов. // Мы попытаемся прочитать содержимое файла SomeText.txt // Если мы не можем открыть файл для чтения его содержимого, cerr << "Uh oh, SomeText.txt could not be opened for reading!" << endl ; // то перемещаем то, что можем прочитать, в строку, а затем выводим эту строку на экран // Когда inf выйдет из области видимости, то деструктор класса ifstream автоматически закроет наш файлРезультат выполнения программы:
Буферизованный вывод
Вывод в языке C++ может быть буферизован. Это означает, что всё, что выводится в файловый поток, не может сразу же быть записанным на диск (в конкретный файл). Это сделано, в первую очередь, по соображениям производительности. Когда данные буфера записываются на диск, то это называется очисткой буфера. Одним из способов очистки буфера является закрытие файла. В таком случае всё содержимое буфера будет перемещено на диск, а затем файл будет закрыт.
Буферизация вывода обычно не является проблемой, но при определенных обстоятельствах она может вызвать проблемы у неосторожных новичков. Например, когда в буфере хранятся данные, а программа преждевременно завершает свое выполнение (либо в результате сбоя, либо путем вызова функции exit()). В таких случаях деструкторы классов файлового ввода/вывода не выполняются, файлы никогда не закрываются, буферы не очищаются и наши данные теряются навсегда. Вот почему хорошей идеей является явное закрытие всех открытых файлов перед вызовом функции exit().
Также буфер можно очистить вручную, используя метод ostream::flush() или отправив std::flush в выходной поток. Любой из этих способов может быть полезен для обеспечения немедленной записи содержимого буфера на диск в случае сбоя программы.
Интересный нюанс: Поскольку std::endl; также очищает выходной поток, то его чрезмерное использование (приводящее к ненужным очисткам буфера) может повлиять на производительность программы (так как очистка буфера в некоторых случаях может быть затратной операцией). По этой причине программисты, которые заботятся о производительности своего кода, часто используют \n вместо std::endl для вставки символа новой строки в выходной поток, дабы избежать ненужной очистки буфера.
Режимы открытия файлов
Что произойдет, если мы попытаемся записать данные в уже существующий файл? Повторный запуск вышеприведенной программы (самая первая) показывает, что исходный файл полностью перезаписывается при повторном запуске программы. А что, если нам нужно добавить данные в конец файла? Оказывается, конструкторы файлового потока принимают необязательный второй параметр, который позволяет указать программисту способ открытия файла. В качестве этого параметра можно передавать следующие флаги (которые находятся в классе ios ):
app — открывает файл в режиме добавления;
ate — переходит в конец файла перед чтением/записью;
binary — открывает файл в бинарном режиме (вместо текстового режима);
in — открывает файл в режиме чтения (по умолчанию для ifstream );
out — открывает файл в режиме записи (по умолчанию для ofstream );
trunc — удаляет файл, если он уже существует.
Можно указать сразу несколько флагов путем использования побитового ИЛИ (|).
ifstream по умолчанию работает в режиме ios::in;
ofstream по умолчанию работает в режиме ios::out;
fstream по умолчанию работает в режиме ios::in ИЛИ ios::out, что означает, что вы можете выполнять как чтение содержимого файла, так и запись данных в файл.
Основной задачей программирования является обработка информации, поэтому любой язык программирования имеет средства для ввода и вывода информации. В языке Си нет операторов ввода-вывода.
Ввод и вывод информации осуществляется через функции стандартной библиотеки. Прототипы рассматриваемых функций находятся в файле stdio.h . Эта библиотека содержит функции
Вывод информации
Функция printf() предназначена для форматированного вывода. Она переводит данные в символьное представление и выводит полученные изображения символов на экран. При этом у программиста имеется возможность форматировать данные, то есть влиять на их представление
на экране.
Общая форма записи функции printf() :
СтрокаФорматов состоит из следующих элементов:
- управляющих символов;
- текста, представленного для непосредственного вывода;
- форматов, предназначенных для вывода значений переменных различных типов.
Объекты могут отсутствовать.
Основные управляющие символы:
- %d — целое число типа int со знаком в десятичной системе счисления;
- %u — целое число типа unsigned int ;
- %x — целое число типа int со знаком в шестнадцатеричной системе счисления;
- %o — целое число типа int со знаком в восьмеричной системе счисления;
- %hd — целое число типа short со знаком в десятичной системе счисления;
- %hu — целое число типа unsigned short ;
- %hx — целое число типа short со знаком в шестнадцатеричной системе счисления;
- %ld — целое число типа long int со знаком в десятичной системе счисления;
- %lu — целое число типа unsigned long int ;
- %lx — целое число типа long int со знаком в шестнадцатеричной системе счисления;
- %f — вещественный формат (числа с плавающей точкой типа float );
- %lf — вещественный формат двойной точности (числа с плавающей точкой типа double );
- %e — вещественный формат в экспоненциальной форме (числа с плавающей точкой типа float в экспоненциальной форме);
- %c — символьный формат;
- %s — строковый формат.
Строка форматов содержит форматы для вывода значений. Каждый формат вывода начинается с символа % . После строки форматов через запятую указываются имена переменных, которые необходимо вывести.
Количество символов % в строке формата должно совпадать с количеством переменных для вывода. Тип каждого формата должен совпадать с типом переменной, которая будет выводиться на это место. Замещение форматов вывода значениями переменных происходит в порядке их следования.
Пример на Си
Результат работы программы
Тот же самый код может быть представлен с использованием одного вызова printf :
Табличный вывод
При указании формата можно явным образом указать общее количество знакомест и количество знакомест, занимаемых дробной частью:
Результат выполнения
В приведенном примере 10 — общее количество знакомест, отводимое под значение переменной; 5 — количество позиций после разделителя целой и дробной части (после десятичной точки). В указанном примере количество знакомест в выводимом числе меньше 10, поэтому свободные знакоместа слева от числа заполняются пробелами. Такой способ форматирования часто используется для построения таблиц.
Ввод информации
Функция форматированного ввода данных с клавиатуры scanf() выполняет чтение данных, вводимых с клавиатуры, преобразует их во внутренний формат и передает вызывающей функции. При этом программист задает правила интерпретации входных данных с помощью спецификаций форматной строки.
Общая форма записи функции scanf( ) :
Строка форматов и список аргументов для функции обязательны.
Результат работы программы:
Функция scanf( ) является функцией незащищенного ввода, т.к. появилась она в ранних версиях языка Си. Поэтому чтобы разрешить работу данной функции в современных компиляторах необходимо в начало программы добавить строчку
2
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
int main() int a[4][5];
int i,j,range,max,maxi,maxj;
float kproiz1,kproiz2;
int proiz1=1;
int proiz2=1;
printf( "введите диапазон заполнения\n" );
scanf( "%d" , &range);
//if (range>=1)
//range=range+5;
printf ( "матрица\n" );
for (i=0; i<5; i++) for (j=0; j<6; j++) a[i][j]=rand() % range+1;
printf( "%d " , a[i][j]);
>
printf( "\n" );
>
max = 1;
for (i=0; i<5; i++) for (j=0; j<6; j++) if (a[i][j] > max) max = a[i][j];
maxi=i;
maxj=j;
>
>
>
printf( "Максимальный элемент матрицы %d[%d;%d] \n" ,max,maxi+1,maxj+1);
proiz1= proiz2 = a[maxi][maxj];
if (maxi<=2) for (i=maxi+1; i<5; i++) if (maxj!=5) for (j=maxj; (j<i+1 && j!=5); j++) proiz1*=a[i][j];
>
>
if (maxj!=0) if (maxj==5) proiz1=proiz1*a[i][maxj];
>
for (j=maxj-1; (j<i+1 && j!=0); j--) proiz1=proiz1*a[i][j];
>
>
>
kproiz1=sqrt(proiz1);
printf( "Среднее геометрическое %f \n" ,kproiz1);
>
if (maxi>=2) for (i=maxi-1; i>0; i--) if (maxj!=5) for (j=maxj; (j<=i+1 && j!=5); j++) proiz2=proiz2*a[i][j];
>
>
if (maxj!=0) if (maxj==5) proiz2=proiz2*a[i][maxj];
>
for (j=maxj-1; (j<=i+1 && j!=0); j--) proiz2*=a[i][j];
>
>
>
kproiz2=sqrt(proiz2);
printf( "Среднее геометрическое %f" ,kproiz2);
>
return 0;
>
Читайте также: