Как передать файл в функцию c
Компилируется и линкуется в линуксе при помощи c++ тихо; файл data.txt существует с полным доступом. При вызове ф-ции fopen() в ф-ции main() программа работает. Посему считаю, что ошибка из-за неправильно передачи *fp.
Белик Виталий 🙂
Регистрация: 23.07.2007
Сообщений: 57,792
В Windows твой код работает без ошибок.
I’m learning to live.
Участник клуба
Регистрация: 15.07.2008
Сообщений: 1,933
Сообщение от v01d
Программа со следующим кодом вываливается с ошибкой сегментирования
Компилируется и линкуется в линуксе при помощи c++ тихо; файл data.txt существует с полным доступом. При вызове ф-ции fopen() в ф-ции main() программа работает. Посему считаю, что ошибка из-за неправильно передачи *fp.
думаю это потому что вы пытаетесь закрыть файл по неинициализированному указателю. Нужно так:
#include void initFile(FILE *); int main(void) < FILE *fp; initFile(&fp); fclose(fp); return 0; >void initFile(FILE **fp)
Пользователь
Регистрация: 22.11.2008
Сообщений: 33
Да, спасибо, разобрался. Аж стыдно стало.
А почему в виндовсе-то работает?
Участник клуба
Регистрация: 15.07.2008
Сообщений: 1,933
Сообщение от v01d
Да, спасибо, разобрался. Аж стыдно стало.
А почему в виндовсе-то работает?
У меня и в виндовс не работает
Пользователь
Регистрация: 30.05.2009
Сообщений: 46
fp из main и fp параметр функции initFile это разные переменные(!), которые при вызове
initFile(fp); // fp(параметр функции initFile) = fp(из функции main)
просто начинают указывать на одну и ту же область памяти. С помощью «параметра fp»(в данном случае) можно менять значение того, на что указывает «fp из main», но изменить значение самого «fp из main» невозможно . Т.е., для того чтобы изменить значение обычной переменной используется указатель, а для изменения значения указателя используется указатель на указатель, как и было показано выше )
Регистрация: 05.10.2009
Сообщений: 3
похожая проблема, делаю вроде как надо.
до кучи, собрал пример приведенный выше netrino:
gcc e1.c e1.c: In function âmainâ: e1.c:8: warning: passing argument 1 of âinitFileâ from incompatible pointer type e1.c: At top level: e1.c:15: error: conflicting types for âinitFileâ e1.c:3: error: previous declaration of âinitFileâ was here
gcc -v Using built-in specs. Target: i386-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enabl e-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c+ +,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jr e --with-cpu=generic --host=i386-redhat-linux Thread model: posix gcc version 4.1.2 20070626 (Red Hat 4.1.2-14)
Как передать имя файла в качестве параметра функции main()? (л.20, примеры 5, 12)
Как сохранить при обработке файла невидимые символы и отредактировать их? (л.20, пример 6)
Как передать файл в функцию? (л.20, пример 7) примеры стр.2-3
int write_to_text_file (ofstream& );
const int len = 81;
ofstream fout (filename, ios::out ); //ofstream fout;
ifstream fin (filename, ios::in );
_getch(); return 0; >
(Вывод в файл)
int write_to_text_file (ofstream & fout)
while (cin.getline (line, len))
(Чтение из файла)
int read_text_file (ifstream & fin)
if (fin.eof()) break;
Как сделать массив файлов и записать туда что-то? (л.20, пример 8) примеры стр.17
(Функция заполнения массива структур из файла)
int fill_record(ifstream &fin, Man* dbase)
char buf[l_buf]; //строка для считывания из файла
strncpy (dbase[i].name, buf, l_name); //заполнение элемента dbase
if (i>=l_dbase) return -1;
return i-1; //количество считанных из файла записей
Как определить количество записей в бинарном файле и выполнить их корректировку? (л.20, пример 14)
При каждом запуске программы мы открываем файл для определения количества записанных в него записей, и в последующую будем записывать структуру с новыми полученными параметрами.
/—получение количества записей в файле—————————
FILE* f = fopen(«file.bin», «rb»);
Ввод данных из файла и вывод в файл
До этого при вводе-выводе данных мы работали со стандартными потоками — клавиатурой и монитором. Теперь рассмотрим, как в языке C реализовано получение данных из файлов и запись их туда. Перед тем как выполнять эти операции, надо открыть файл и получить доступ к нему.
В языке программирования C указатель на файл имеет тип FILE и его объявление выглядит так:
FILE *myfile;
С другой стороны, функция fopen() открывает файл по указанному в качестве первого аргумента адресу в режиме чтения («r»), записи («w») или добавления («a») и возвращает в программу указатель на него. Поэтому процесс открытия файла и подключения его к программе выглядит примерно так:
myfile = fopen("hello.txt", "r");
Примечание. В случае использования относительной адресации текущим/рабочим каталогом в момент исполнения программы должен быть тот, относительно которого указанный относительный адрес корректен. Место нахождения самого исполняемого файла не важно.
При чтении или записи данных в файл обращение к нему осуществляется посредством файлового указателя (в данном случае, myfile).
Если в силу тех или иных причин (нет файла по указанному адресу, запрещен доступ к нему) функция fopen() не может открыть файл, то она возвращает NULL. В реальных программах почти всегда обрабатывают ошибку открытия файла в ветке if , мы же далее опустим это.
Объявление функции fopen() содержится в заголовочном файле stdio.h, поэтому требуется его подключение. Также в stdio.h объявлен тип-структура FILE.
После того, как работа с файлом закончена, принято его закрывать, чтобы освободить буфер от данных и по другим причинам. Это особенно важно, если после работы с файлом программа продолжает выполняться. Разрыв связи между внешним файлом и указателем на него из программы выполняется с помощью функции fclose() . В качестве параметра ей передается указатель на файл:
fclose(myfile);
В программе может быть открыт не один файл. В таком случае каждый файл должен быть связан со своим файловым указателем. Однако если программа сначала работает с одним файлом, потом закрывает его, то указатель можно использовать для открытия второго файла.
Чтение из текстового файла и запись в него
fscanf()
Функция fscanf() аналогична по смыслу функции scanf() , но в отличии от нее осуществляет форматированный ввод из файла, а не стандартного потока ввода. Функция fscanf() принимает параметры: файловый указатель, строку формата, адреса областей памяти для записи данных:
fscanf(myfile, "%s%d", str, &a);
Возвращает количество удачно считанных данных или EOF. Пробелы, символы перехода на новую строку учитываются как разделители данных.
Допустим, у нас есть файл содержащий такое описание объектов:
apples 10 23.4 bananas 5 25.0 bread 1 10.3
Тогда, чтобы считать эти данные, мы можем написать такую программу:
#include int main () { FILE *file; struct food { char name[20]; unsigned qty; float price; }; struct food shop[10]; char i=0; file = fopen("fscanf.txt", "r"); while (fscanf(file, "%s%u%f", shop[i].name, &(shop[i].qty), &(shop[i].price)) != EOF) { printf("%s %u %.2f\n", shop[i].name, shop[i].qty, shop[i].price); i++; } }
В данном случае объявляется структура и массив структур. Каждая строка из файла соответствует одному элементу массива; элемент массива представляет собой структуру, содержащую строковое и два числовых поля. За одну итерацию цикл считывает одну строку. Когда встречается конец файла fscanf() возвращает значение EOF и цикл завершается.
fgets()
Функция fgets() аналогична функции gets() и осуществляет построчный ввод из файла. Один вызов fgets() позволят прочитать одну строку. При этом можно прочитать не всю строку, а лишь ее часть от начала. Параметры fgets() выглядят таким образом:
fgets(массив_символов, количество_считываемых_символов, указатель_на_файл)
fgets(str, 50, myfile)
Такой вызов функции прочитает из файла, связанного с указателем myfile, одну строку текста полностью, если ее длина меньше 50 символов с учетом символа ‘\n’, который функция также сохранит в массиве. Последним (50-ым) элементом массива str будет символ ‘\0’, добавленный fgets() . Если строка окажется длиннее, то функция прочитает 49 символов и в конце запишет ‘\0’. В таком случае ‘\n’ в считанной строке содержаться не будет.
#include #define N 80 main () { FILE *file; char arr[N]; file = fopen("fscanf.txt", "r"); while (fgets(arr, N, file) != NULL) printf("%s", arr); printf("\n"); fclose(file); }
В этой программе в отличие от предыдущей данные считываются строка за строкой в массив arr. Когда считывается следующая строка, предыдущая теряется. Функция fgets() возвращает NULL в случае, если не может прочитать следующую строку.
getc() или fgetc()
Функция getc() или fgetc() (работает и то и другое) позволяет получить из файла очередной один символ.
#include #define N 80 int main () { FILE *file; char arr[N]; int i; file = fopen("fscanf.txt", "r"); while ((arr[i] = fgetc(file)) != EOF) { if (arr[i] == '\n') { arr[i] = '\0'; printf("%s\n",arr); i = 0; } else i++; } arr[i] = '\0'; printf("%s\n",arr); fclose(file); }
Приведенный в качестве примера код выводит данные из файла на экран.
Запись в текстовый файл
Также как и ввод, вывод в файл может быть различным.
- Форматированный вывод. Функция fprintf ( файловый_указатель, строка_формата, переменные ) .
- Посточный вывод. Функция fputs ( строка, файловый_указатель ) .
- Посимвольный вывод. Функция fputc() или putc( символ, файловый_указатель ) .
Ниже приводятся примеры кода, в которых используются три способа вывода данных в файл.
Запись в каждую строку файла полей одной структуры:
#include int main () { FILE *file; struct food { char name[20]; unsigned qty; float price; }; struct food shop[10]; char i=0; file = fopen("fprintf.txt", "w"); while (scanf("%s%u%f", shop[i].name, &(shop[i].qty), &(shop[i].price)) != EOF) { fprintf(file, "%s %u %.2f\n", shop[i].name, shop[i].qty, shop[i].price); i++; } fclose(file); }
Построчный вывод в файл ( fputs() , в отличие от puts() сама не помещает в конце строки ‘\n’):
while (gets(arr) != NULL) { fputs(arr, file); fputs("\n", file); }
Пример посимвольного вывода:
while ((i = getchar()) != EOF) putc(i, file);
Чтение из двоичного файла и запись в него
С файлом можно работать не как с последовательностью символов, а как с последовательностью байтов. В принципе, с нетекстовыми файлами работать по-другому не возможно. Однако так можно читать и писать и в текстовые файлы. Преимущество такого способа доступа к файлу заключается в скорости чтения-записи: за одно обращение можно считать/записать существенный блок информации.
При открытии файла для двоичного доступа, вторым параметром функции fopen() является строка «rb» или «wb».
Тема о работе с двоичными файлами достаточно сложная, для ее изучения требуется отдельный урок. Здесь будут отмечены только особенности функций чтения-записи в файл, который рассматривается как поток байтов.
Функции fread() и fwrite() принимают в качестве параметров:
- адрес области памяти, куда данные записываются или откуда считываются,
- размер одного данного какого-либо типа,
- количество считываемых данных указанного размера,
- файловый указатель.
Эти функции возвращают количество успешно прочитанных или записанных данных. Т.е. можно «заказать» считывание 50 элементов данных, а получить только 10. Ошибки при этом не возникнет.
Пример использования функций fread() и fwrite() :
#include #include int main () { FILE *file; char shelf1[50], shelf2[100]; int n, m; file = fopen("shelf1.txt", "rb"); n=fread(shelf1, sizeof(char), 50, file); fclose(file); file = fopen("shelf2.txt", "rb"); m=fread(shelf2, sizeof(char), 50, file); fclose(file); shelf1[n] = '\0'; shelf2[m] = '\n'; shelf2[m+1] = '\0'; file = fopen("shop.txt", "wb"); fwrite(strcat(shelf2,shelf1), sizeof(char), n+m, file); fclose(file); }
Здесь осуществляется попытка чтения из первого файла 50-ти символов. В n сохраняется количество реально считанных символов. Значение n может быть равно 50 или меньше. Данные помещаются в строку. То же самое происходит со вторым файлом. Далее первая строка присоединяется ко второй, и данные сбрасываются в третий файл.
- Напишите программу, которая запрашивает у пользователя имя (адрес) текстового файла, далее открывает его и считает в нем количество символов и строк.
- Напишите программу, которая записывает в файл данные, полученные из другого файла и так или иначе измененные перед записью. Каждая строка данных, полученная из файла, должна помещаться в структуру.
Курс с решением части задач:
pdf-версия
Работа с файлами в C++. Часть 1 – Библиотека fstream.
Хедер fstream предоставляет функционал для считывания данных из файла и для записи в файл. В целом он очень похож на хедер iostream , который работает с консолью, поскольку консоль это тоже файл. Поэтому все основные операции такие же, за мелкими отличиями, как в предыдущей теме по iostream.
Наиболее частые операции следующее:
-
- Методы проверки открыт ли файл is_open() и достигнут ли конец файла eof()
- Операции позиционирования tellg(), tellp() и seekg(), seekp()
Это не все возможности, которые предоставляет библиотека fstream. Рассматривать все сейчас мы не будем, поскольку их круг применения достаточно узок. Познакомимся с вышеперечисленными. Начнем с класса чтения.
Класс ifstream
Предоставляет возможности для чтения файлов. Открыть файл можно двумя способами: вызвав метод open() или указав путь к нему в конструкторе. Вам необходимо подготовить текстовый файл, перед тем, как начать набирать код. На диске d создайте папку с именем 1 и в ней создайте файл с расширением txt – “файл.txt”.
Открытие файла методом open()
#include// подключаем библиотеку
using namespace std ;
ifstream file ; // создаем объект класса ifstream
file . open ( «d:\\1\\файл.txt» ) ; // открываем файлОткрытие файла в конструкторе выглядит так:
#include
// подключаем библиотеку
using namespace std ;
ifstream file ( «d:\\1\\файл.txt» ) ; // открываем файл в конструктореТак мы просим открыть файл txt с именем файл.txt, который лежит в папке с названием 1, а папка находится на диске d.
Использование метода open() удобно, если программист не хочет сразу привязываться к файлу. Вдруг нужно свойство класса или глобальную переменную, ну а открывать файл уже потом. Если же нужно открыть файл внутри некой функции, поработать с ним и закрыть, то можно прописать путь к файлу прямо в конструкторе. В общем зависит от ситуации.
Открыв файл, желательно прописать проверку: открылся ли он? Так как есть ряд причин, по которым файл может не открыться, а мы этого не увидим. Например, файла с указанным именем нет в прописанной папке или путь указан неверно. Можно пойти двумя путями: проверить переменную файла в логическом выражении (применив оператор “!”, к примеру) или использовать метод is_open() :
Проверка открытия файла условием if
using namespace std ;
setlocale ( LC_ALL , «rus» ) ;
ifstream file ( «d:\\1\\файл.txt» ) ;
cout << "Файл не открыт\n\n" ; cout << "Все ОК! Файл открыт!\n\n" ;Так все отработает нормально и файл откроется:
Теперь попробуйте вписать название папки не 1, а 2 ifstream file ( «d:\\color : #ff0000;»>2 \\файл.txt”); и снова запустите программу. Так как папки с указанным именем мы не создавали, то и файл, естественно, не может быть открыт:
Второй вариант проверки с использованием метода is_open() :
Проверка методом is_open()
using namespace std ;
setlocale ( LC_ALL , «rus» ) ;
ifstream file ( «d:\\1\\файл.txt» ) ;
if ( file . is_open ( ) ) // вызов метода is_open()
cout << "Все ОК! Файл открыт!\n\n" << endl ; cout << "Файл не открыт!\n\n" << endl ;Метод is_open() вернет 1, если файл найден и успешно открыт. Иначе вернет 0 и сработает код прописанный в блоке else .
Если файл не открыт – желательно обработать ошибку. Как правило, если вся работа программы связана с файлом пишут некое сообщение в консоль, и ставят выход из программы. При серьезных ошибках принято возвращать некий код выполнения (число), который будет характеризовать ту или иную ошибку. Коды для каждого вида ошибок автор программы может придумывать свои. Один из способов обработки ошибок в программе мы рассматривали в статье Исключения в С++.
Если файл успешно открыт, из него можно производить чтение.
Оператор считывания >>
Так же как и в iostream считывание можно организовать оператором >> , который указывает в какую переменную будет произведено считывание:
file >> d >> i >> s ;
Считает вещественное, целое и строку. Считывание строки закончится, если появится пробел или конец строки. Стоит отметить, что оператор >> применяется к текстовым файлам. Считывание из бинарного файла производить лучше всего с помощью метода read().
Кстати этот оператор достаточно удобен, если стоит задача разделить файл на слова:
// Считка слов из файла
for ( file >> s ; ! file . eof ( ) ; file >> s )
cout << s << endl ;Методы getline() и get()
Считывание целой строки до перевода каретки производится так же как и в iostream методом getline(). Причем рекомендуется использовать его переопределеную версию в виде функции, если считывается строка типа string:
//Считка строки из текста
getline ( file , s ) ;
cout << s << endl ;Если же читать нужно в массив символов char[], то либо get() либо getline() именно как методы:
//Создаем буффер для чтения
char * buffer = new char [ n + 1 ] ;
buffer [ n ] = 0 ;
//Читаем n символов
file . get ( buffer , n ) ;
//Или так, но до первого пробела
file . getline ( buffer , n , ‘ ‘ ) ;
//выводим считанное
cout << buffer ; //Освобождаем буффер delete [ ] buffer ;Принцип в общем тот же, что и в аналогах из iostream: Указывается в параметрах буфер (переменная, куда будет производиться чтение), или точнее указатель на блок памяти (если переменная объявлена статически: char buffer[255] к примеру, то пишется в параметры &buffer), указывается максимальное количество считываемого (в примере это n), дабы не произошло переполнение и выход за пределы буфера и по необходимости символ-разделитель, до которого будет считка (в примере это пробел). Надеюсь я не больно наступлю на хобот фанатикам Си, если сажу что эти две функции на 99% взаимозаменяемы, и на 95% могут быть заменены методом read() .
Метод read()
//Считка из файла N байт
//Создаем буффер
char * buffer = new char [ n + 1 ] ; buffer [ n ] = 0 ;
//Читаем в него байты
file . read ( buffer , n ) ;
//выводим их на экран
cout << buffer ; delete [ ] buffer ;Похож на предыдущий пример?
Собственно тут тот же результат – считается указанное количество символов. Исключение только в том, что нельзя указать разделитель. read() применяется для неформатированного ввода. Призван в первую очередь читать бинарные файлы. Поскольку текстовый файл – частный случай бинарного, этот метод вполне применим и к текстовому файлу.
Метод close()
Закрывает файл. Даже добавить нечего. Единственная пожалуй ремарка – от того, что файл, открытый для чтения, не будет закрыт этим методом как правило хуже не станет. Очень редки ситуации, когда открытый для чтения файл портится, если завершить программу не закрывая файл. Связана эта порча прежде всего с нестандартными устройствами типа стримеров на магнитной ленте или каких нибудь потоковых хитрых промышленных контроллерах, но по феншую стоит запомнить – открытый файл должен быть закрыт. Это считается хорошим тоном.
Метод eof()
Проверяет не достигнут ли конец файла. Т.е. можно ли из него продолжать чтение. Выше пример с считкой слов оператором >> как раз использует такую проверку.
Метод seekg()
Производит установку текущей позиции в нужную, указываемую числом. В этот метод так же передается способ позиционирования:
-
- ios_base::end – Отсчитать новую позицию с конца файла
-
- ios_base::beg – Отсчитать новую позицию с начала файла (абсолютное позиционирование)
- ios_base::cur – Перескочить на n байт начиная от текущей позиции в файле (по умолчанию)