Не создается файл си

Обновлено: 05.07.2024

В UNIX и в MS DOS файлы не имеют предопределенной структуры и представляют собой просто линейные массивы байт. Если вы хотите задать некоторую структуру хранимой информации - вы должны позаботиться об этом в своей программе сами.

  • они могут изменять свой размер;
  • обращение к элементам этих массивов производится не при помощи операции индексации [], а при помощи специальных системных вызовов и функций;
  • доступ к элементам файла происходит в так называемой "позиции чтения/записи", которая автоматически продвигается при операциях чтения/записи, т.е. файл просматривается последовательно. Есть, правда, функции для произвольного изменения этой позиции.

Файлы имеют имена и организованы в иерархическую древовидную структуру из каталогов и простых файлов. Об этом и о системе именования файлов прочитайте в документации по UNIX.

Для работы с каким-либо файлом наша программа должна открыть этот файл - установить связь между именем файла и некоторой переменной в программе. При открытии файла в ядре операционной системы выделяется "связующая" структура file "открытый файл", содержащая:
f_offset: указатель позиции чтения/записи, который в дальнейшем мы будем обозначать как
RWptr. Это long-число, равное расстоянию в байтах от начала файла до позиции чтения/записи;
f_flag: режимы открытия файла: чтение, запись, чтение и запись, некоторые дополнительные флаги;
f_inode: расположение файла на диске (в UNIX - в виде ссылки на I-узел файла * );
и кое-что еще.

У каждого процесса имеется таблица открытых им файлов - это массив ссылок на упомянутые "связующие" структуры ** . При открытии файла в этой таблице ищется

Содержимое некоторых полей этого паспорта можно узнать вызовом stat(). Все I-узлы собраны в единую область в начале файловой системы - так называемый I-файл. Все Iузлы пронумерованы, начиная с номера 1. Корневой каталог (файл с именем "/") как правило имеет I-узел номер 2.

** - У каждого процесса в UNIX также есть свой "паспорт". Часть этого паспорта находится в таблице процессов в ядре ОС, а часть - "приклеена" к самому процессу, однако не доступна из программы непосредственно. Эта вторая часть паспорта носит название "u-area" или структура user. В нее, в частности, входят таблица открытых процессом файлов, свободная ячейка, в нее заносится ссылка на структуру "открытый файл" в ядре, и ИНДЕКС этой ячейки выдается в вашу программу в виде целого числа - так называемого "дескриптора файла".

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

Дескрипторы являются локальными для каждой программы. Т.е. если две программы открыли один и тот же файл - дескрипторы этого файла в каждой из них не обязательно совпадут (хотя и могут). Обратно: одинаковые дескрипторы (номера) в разных программах не обязательно обозначают один и тот же файл. Следует учесть и еще одну вещь: несколько или один процессов могут открыть один и тот же файл одновременно несколько раз. При этом будет создано несколько "связующих" структур (по одной для каждого открытия); каждая из них будет иметь СВОЙ указатель чтения/записи. Возможна и ситуация, когда несколько дескрипторов ссылаются к одной структуре - смотри ниже описание вызова dup2. Параметр как_открыть: Если файл еще не существовал, то его нельзя открыть: open вернет значение (-1), ссылка на I-узел текущего каталога а также ссылка на часть паспорта в таблице процессов сигнализирующее об ошибке. В этом случае файл надо создать:

Дескриптор fd будет открыт для записи в этот новый пустой файл. Если же файл уже существовал, creat опустошает его, т.е. уничтожает его прежнее содержимое и делает его длину равной 0L байт. Коды_доступа задают права пользователей на доступ к файлу. Это число задает битовую шкалу из 9и бит, соответствующих строке

Первая группа - эта права владельца файла, вторая - членов его группы, третяя - всех прочих. Эти коды для владельца файла имеют еще и мнемонические имена (используемые в вызове stat):

Подробности - в руководствах по системе UNIX. Отметим в частности, что open() может вернуть код ошибки fd < 0 не только в случае, когда файл не существует (errno==ENOENT), но и в случае, когда вам не разрешен соответствующий доступ к этому файлу (errno==EACCES; про переменную кода ошибки errno см. в главе "Взаимодействие с UNIX").

Вызов creat - это просто разновидность вызова open в форме O_TRUNC означает, что если файл уже существует, то он должен быть опустошен при открытии. Коды доступа и владелец не изменяются. O_CREAT означает, что файл должен быть создан, если его не было (без этого флага файл не создастся, а open вернет fd < 0). Этот флаг требует задания третьего аргумента коды_доступа *** . Если файл уже существует - этот флаг не имеет никакого эффекта, но зато вступает в действие O_TRUNC.

Существует также флаг O_EXCL который может использоваться совместно с O_CREAT. Он делает следующее: если файл уже существует, open вернет код ошибки (errno==EEXIST). Если файл не

*** - Заметим, что на самом деле коды доступа у нового файла будут равны di_mode = (коды_доступа &

u_cmask) | IFREG; (для каталога вместо IFREG будет IFDIR), где маска u_cmask задается системным вызовом umask(u_cmask); (вызов выдает прежнее значение маски) и в дальнейшем наследуется всеми потомками данного процесса (она хранится в u-area процесса). Эта маска позволяет запретить доступ к определенным операциям для всех создаваемых нами файлов, несмотря на явно заданные коды доступа, например umask(0077); /* . ------ */ делает значащими только первые 3 бита кодов доступа (для владельца файла). Остальные биты будут равны нулю.

Все это относится и к созданию каталогов вызовом mkdir. существовал - срабатывает O_CREAT и файл создается. Это позволяет предохранить уже существующие файлы от уничтожения. Файл удаляется при помощи У каждой программы по умолчанию открыты три первых дескриптора, обычно связанные Если при вызове close(fd) дескриптор fd не соответствует открытому файлу (не был открыт) - ничего не происходит.

Напишите программу, которая копирует содержимое одного файла в другой (новый) файл. При этом используйте системные вызовы чтения и записи read и write. Эти сисвызовы пересылают массивы байт из памяти в файл и наоборот. Но любую переменную можно рассматривать как массив байт, если забыть о структуре данных в переменной!
  • При использовании write() и read() надо передавать АДРЕС данного, которое мы хотим записать в файл (места, куда мы хотим прочитать данные из файла).
  • Операции read и write возвращают число действительно прочитанных/записанных байт (при записи оно может быть меньше указанного нами, если на диске не хватает места; при чтении - если от позиции чтения до конца файла содержится меньше информации, чем мы затребовали).
  • Операции read/write продвигают указатель чтения/записи При открытии файла указатель стоит на начале файла: RWptr=0. При записи файл если надо автоматически увеличивает свой размер. При чтении - если мы достигнем конца файла, то read будет возвращать "прочитано 0 байт" (т.е. при чтении указатель чтения не может стать больше размера файла).
  • Аргумент сколькоБайт имеет тип unsigned, а не просто int: Приведем упрощенные схемы логики этих сисвызовов, когда они работают с обычным дисковым файлом (в UNIXустройства тоже выглядят для программ как файлы, но иногда с особыми свойствами):

4.2.1. m = write(fd, addr, n);

4.2.2. m = read(fd, addr, n);

В системном вызове write второй аргумент должен быть адресом данного, которое мы хотим записать в файл. Поэтому мы должны были написать &c (во втором вызове write).

Ошибка в scanf - указание значения переменной вместо ее адреса - является довольно распространенной и не может быть обнаружена компилятором (даже при использовании прототипа функции scanf(char *fmt, . ), так как scanf - функция с переменным числом аргументов заранее не определенных типов). Приходится полагаться исключительно на собственную внимательность!

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

Константа NOFILE означает максимальное число одновременно открытых файлов для одного процесса (это размер таблицы открытых процессом файлов, таблицы дескрипторов). Изучите описание системного вызова fcntl (file control).

Напишите функцию rename() для переименования файла. Указание: используйте системные вызовы link() и unlink(). Ответ: Вызов link(существующее_имя, новое_имя); создает файлу альтернативное имя - в UNIX файл может иметь несколько имен: так каждый каталог имеет какое-то имя в родительском каталоге, а также имя "." в себе самом.

Каталог же, содержащий подкаталоги, имеет некоторое имя в своем родительском каталоге, имя "." в себе самом, и по одному имени ".." в каждом из своих подкаталогов.

Этот вызов будет неудачен, если файл новое_имя уже существует; а также если мы попытаемся создать альтернативное имя в другой файловой системе. Вызов unlink(имя_файла) удаляет имя файла. Если файл больше не имеет имен - он уничтожается. Здесь есть одна тонкость: рассмотрим фрагмент Первый оператор создает пустой файл. Затем мы открываем файл и уничтожаем его единственное имя. Но поскольку есть программа, открывшая этот файл, он не удаляется немедленно! Программа далее работает с безымянным файлом при помощи дескриптора fd. Как только файл закрывается - он будет уничтожен системой (как не имеющий имен). Такой трюк используется для создания временных рабочих файлов.

Файл можно удалить из каталога только в том случае, если данный каталог имеет для вас код доступа "запись". Коды доступа самого файла при удалении не играют роли.

В современных версиях UNIX есть системный вызов rename, который делает то же самое, что и написанная нами одноименная функция.

Еще один пример: программа a.out желает запустить программу /usr/bin/vi (смотри про функцию system() сноску через несколько страниц): На вашей же машине редактор vi помещен в /usr/local/bin/vi. Тогда вы просто создаете альтернативное имя этому редактору:

Помните, что альтернативное имя файлу можно создать лишь в той же файловой системе, где содержится исходное имя. В семействе BSD **** это ограничение можно обойти, создав "символьную ссылку" вызовом

Символьная ссылка - это файл, содержащий имя другого файла (или каталога). Система не производит автоматический подсчет числа таких ссылок, поэтому возможны "висячие" ссылки - указывающие на уже удаленный файл. Прочесть содержимое файла-ссылки можно системным вызовом

Системный вызов stat автоматически разыменовывает символьные ссылки и выдает информацию про указуемый файл. Системный вызов lstat (аналог stat за исключением названия) выдает информацию про саму ссылку (тип файла S_IFLNK). Коды доступа к ссылке не имеют никакого значения для системы, существенны только коды доступа самого указуемого файла.

Еще раз: символьные ссылки удобны для указания файлов и каталогов на другом диске. Пусть у вас не помещается на диск каталог /opt/wawa. Вы можете разместить каталог wawa на диске USR: /usr/wawa. После чего создать символьную ссылку из /opt: чтобы программы видели этот каталог под его прежним именем /opt/wawa.

Еще раз: hard link - то, что создается системным вызовом link, имеет тот же I-node (индексный узел, паспорт), что и исходный файл. Это просто альтернативное имя файла, учитываемое в поле di_nlink в I-node. symbolic link - создается вызовом symlink. Это отдельный самостоятельный файл, с собственным I-node. Правда, коды доступа к этому файлу не играют никакой роли; значимы только коды доступа указуемого файла.

  • lseek(fd, offset, whence) устанавливает указатель чтения/записи на расстояние offset байт при whence: Эти значения whence можно обозначать именами:
  • Установка указателя чтения/записи - это виртуальная операция, т.е. реального подвода магнитных головок и вообще обращения к диску она не вызывает. Реальное движение головок к нужному месту диска произойдет только при операциях чтения/записи read()/write(). Поэтому lseek() - дешевая операция.
  • lseek() возвращает новую позицию указателя чтения/записи RWptr относительно начала файла (long смещение в байтах). Помните, что если вы используете это значение, то вы должны предварительно описать lseek как функцию, возвращающую длинное целое: long lseek();
  • Аргумент offset должен иметь тип long (не ошибитесь!).
  • Если поставить указатель за конец файла (это допустимо!), то операция записи write() сначала заполнит байтом '\0' все пространство от конца файла до позиции указателя; операция read() при попытке чтения из-за конца файла вернет "прочитано 0 байт". Попытка поставить указатель перед началом файла вызовет ошибку.
  • Вызов lseek() неприменим к pipe и FIFO-файлам, поэтому попытка сдвинуться на 0 байт выдаст ошибку: выдает "истину", если fd - дескриптор "трубы"(pipe).

Напомним, что при записи в файл, его длина автоматически увеличивается, когда мы записываем информацию за прежним концом файла. Это вызывает отведение места на диске для хранения новых данных (порциями, называемыми блоками - размером от 1/2 до 8 Кб в разных версиях). Таким образом, размер файла ограничен только наличием свободных блоков на диске.

В нашем примере получится файл длиной 1024003 байта. Будет ли он занимать на диске 1001 блок (по 1 Кб)?

  • Блоки располагаются на диске не обязательно подряд - у каждого файла есть специальным образом организованная таблица адресов его блоков.
  • Последний блок файла может быть занят не целиком (если длина файла не кратна размеру блока), тем не менее число блоков у файла всегда целое (кроме семейства BSD, где блок может делиться на фрагменты, принадлежащие разным файлам). Операционная система в каждый момент времени знает длину файла с точностью до одного байта и не позволяет нам "заглядывать" в остаток блока, пока при своем "росте" файл не займет эти байты.
  • Блок на диске физически выделяется лишь после операции записи в этот блок.

В нашем примере: при создании файла его размер 0, и ему выделено 0 блоков. При первой записи файлу будет выделен один блок (логический блок номер 0 для файла) и в его начало запишется "begin". Длина файла станет равна 5 (остаток блока - 1019 байт - не используется и файлу логически не принадлежит!). Затем lseek поставит указатель записи далеко за конец файла и write запишет в 1000-ый блок слово "end". 1000-ый блок будет выделен на диске. В этот момент у файла "возникнут" и все промежуточные блоки 1..999. Однако они будут только "числиться за файлом", но на диске отведены не будут (в таблице блоков файла это обозначается адресом 0)! При чтении из них будут читаться байты '\0'. Это так называемая "дырка" в файле. Файл имеет размер 1024003 байта, но на диске занимает всего 2 блока (на самом деле чуть больше, т.к. часть таблицы блоков файла тоже находится в специальных блоках файла). Блок из "дырки" станет реальным, если в него что-нибудь записать.

Будьте готовы к тому, что "размер файла" (который, кстати, можно узнать системным вызовом stat) - это в UNIX не то же самое, что "место, занимаемое файлом на диске".

Ответ: используется системный вызов open() вместо функции fopen(); а также close вместо fclose, а их форматы (и результат) различаются! Следует четко различать две существующие в Си модели обмена с файлами: через системные вызовы: open, creat, close, read, write, lseek; и через библиотеку буферизованного обмена stdio: fopen, fclose, fread, fwrite, fseek, getchar, putchar, printf, и.т.д. В первой из них обращение к файлу происходит по целому fd - дескриптору файла, а во втором - по указателю FILE *fp - указателю на файл. Это параллельные механизмы (по своим возможностям), хотя второй является просто надстройкой над первым. Тем не менее, лучше их не смешивать.

* I-узел (I-node, индексный узел) - своеобразный "паспорт", который есть у каждого файла (в том числе и каталога). В нем содержатся:

** BSD - семейство UNIX-ов из University of California, Berkley. Berkley Software Distribution.

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

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

Как сделать из функции на цикле такую же с рекурсией? Как вообще переделать while/do-while на рекурсию?

__________________
Помощь в написании контрольных, курсовых и дипломных работ здесь

Почему БЕЗОШИБОЧНО создаётся файл, а его не видно?
Друзья! Вот простой код, создаётся файл, а имя для него генерит функция tmpnam; Но файла не.


Fstream: при запуске программы в IDE, файл не создаётся; почему так происходит?
Доброго времени суток!) Может немного глупый вопрос, но самому решить не получается. Когда собираю.

Почему файл пустой?
Вроде бы делаю всё верно. Объявлен массив структур struct book < int index; char title; .

Может быть Ваш файл на столько мал, что целиком помещается в дисковый буфер? В таких случаях фактическая запись происходит только при закрытии файла. Вы проверили дохождение до fclose? я же говорю, всё работает, в дебаггере имеются значения массива, который я пишу в файл, указатели не нулевые, иначе бы дело не дошло до записи, но реально на диске никакого файла нет, вся функция отрабатывает до выхода, закрывает файл и передаёт уравление следующей, где программа падает, поскольку файла на самом деле нет и читать просто нечего При чём здесь вообще функция? Проверять надо закрытие файла. Вы на дебаге видели, что до fclose доходит? Выход из функции с закрытием файла вообще то не связан. вам бы посты понабивать или чего вы хотите? Я всё это проверял именно в дебаггере Кстати, что за режим такой wt? w есть - запись, w+ есть - перезапись. А wt что такое?

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

Добавлено через 1 минуту
Способ использования файла задается следующими символами:

r - открыть существующий файл для чтения;
w - создать новый файл для записи (если файл с указанным именем существует, то он будет переписан);
а - дополнить файл (открыть существующий файл для записи информации, начиная с конца файла, или создать файл, если он не существует);

r - чтение: открыть файл для ввода данных. Файл должен существовать.
w - запись: создаёт пустой файл для вывода данных. Если файл с таким именем уже существует, то его содержимое очищается,
и файл рассматривается как новый пустой файл.
a - дополнение: открыть файл для вывода/записи в конец файла. Операции вывода всегда записывают данные в конец файла, расширяя его.
Перестановка операций (fseek, fsetpos, rewind) игнорируются. Файл создается, если он не существует.

r+ - открыть существующий файл для чтения и записи;
w+ - создать новый файл для чтения и записи;
a+ - дополнить или создать файл с возможностью чтения и записи;

rb - открыть двоичный файл для чтения;
wb - создать двоичный файл для записи;
аb - дополнить двоичный файл;

r+b - открыть двоичный файл для чтения и записи;
w+b - создать двоичный файл для чтения и записи;
а+b - дополнить двоичный файл с предоставлением возможности чтения и записи;

rt - открыть текстовой файл для чтения;
wt - создать текстовый файл для записи;
at - дополнить текстовый файл;

r+t - открыть текстовой файл для чтения и записи;
w+t - создать текстовый файл для чтения и записи;
a+t - дополнить текстовый файл с предоставлением возможности записи и чтения.

Если режим t или b не задан (например, r, w или а), то он определяется значением глобальной переменной _fmode. Если fmode=0_BINARY, то файлы открываются в двоичном режиме, а если _fmode=0_TEXT - в текстовом режиме. Константы 0_BINARY и 0_ТЕXТ определены в файле fcntl.h.

Строки вида r+b можно записывать и в другой форме: rb+.

Добавлено через 58 минут
Результат работы программы с генерацией рандомного массива рандомного размера:

Ширина и высота массива [5..65535 или *]: * * **
Ширина: 6 ; Высота: 10
Введите элементы масива [-32768..32767 или * или **]:
Будет сгенерирован случайный массив заданного размера.
Для продолжения нажмите любую клавишу . . .

62 -29 -66 91 72 73
-72 -91 -126 16 -61 35
-22 -20 -75 -125 -22 81
28 74 95 84 -86 -109
105 70 97 -119 -9 12
126 -92 16 64 38 -90
-30 9 -47 83 -116 -71
121 -14 -86 42 -126 -122
-92 -40 103 -52 -74 -67
40 -13 -67 -17 118 29

Максимальные значения в строках массива:
91 35 81 95 105 126 83 121 103 118

Для удобства обращения информация в запоминающих устройствах хранится в виде файлов.

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

Каталог ( папка , директория ) – именованная совокупность байтов на носителе информации, содержащая название подкаталогов и файлов, используется в файловой системе для упрощения организации файлов.

Файловой системой называется функциональная часть операционной системы, обеспечивающая выполнение операций над файлами. Примерами файловых систем являются FAT (FAT – File Allocation Table, таблица размещения файлов), NTFS, UDF (используется на компакт-дисках).

Существуют три основные версии FAT: FAT12, FAT16 и FAT32. Они отличаются разрядностью записей в дисковой структуре, т.е. количеством бит, отведённых для хранения номера кластера. FAT12 применяется в основном для дискет (до 4 кбайт), FAT16 – для дисков малого объёма, FAT32 – для FLASH-накопителей большой емкости (до 32 Гбайт).


Рассмотрим структуру файловой системы на примере FAT32.

Файловая структура FAT32

Устройства внешней памяти в системе FAT32 имеют не байтовую, а блочную адресацию. Запись информации в устройство внешней памяти осуществляется блоками или секторами.

Сектор – минимальная адресуемая единица хранения информации на внешних запоминающих устройствах. Как правило, размер сектора фиксирован и составляет 512 байт. Для увеличения адресного пространства устройств внешней памяти сектора объединяют в группы, называемые кластерами.

Кластер – объединение нескольких секторов, которое может рассматриваться как самостоятельная единица, обладающая определёнными свойствами. Основным свойством кластера является его размер, измеряемый в количестве секторов или количестве байт.

Файловая система FAT32

Файловая система 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.

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

Создает или перезаписывает файл в указанном пути.

Перегрузки

Создает или перезаписывает файл в указанном пути.

Создает или перезаписывает файл по заданному пути с указанием размер буфера.

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

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

Create(String)

Создает или перезаписывает файл в указанном пути.

Параметры

Путь и имя создаваемого файла.

Возвращаемое значение

FileStream, обеспечивающий доступ для чтения и записи к файлу, указанному в path .

Исключения

У вызывающего объекта отсутствует необходимое разрешение.

-или- Параметр path указывает файл, доступный только для чтения.

-или- path указывает файл, который скрыт.

path имеет значение null .

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

Указан недопустимый путь (например, он ведет на несопоставленный диск).

Ошибка ввода-вывода при создании файла.

Параметр path задан в недопустимом формате.

Примеры

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

Комментарии

FileStreamОбъект, созданный этим методом, имеет значение по умолчанию FileShare None ; никакие другие процессы или код не могут получить доступ к созданному файлу, пока не будет закрыт исходный маркер файла.

Этот метод эквивалентен Create(String, Int32) перегрузке метода, используя размер буфера по умолчанию 4 096 байт.

path Параметр может указывать сведения относительного или абсолютного пути. Сведения об относительном пути интерпретируется как относительно текущего рабочего каталога. Сведения о получении текущего рабочего каталога см. в разделе GetCurrentDirectory .

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

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

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

  • 1. Открыть файл, для того, чтобы к нему можно было обращаться. Соответственно, открывать можно для чтения, записи, чтения и записи, переписывания или записи в конец файла и т.п. Когда вы открываете файл, может также произойти куча ошибок – файла может не существовать, это может быть файл не того типа, у вас может не быть прав на работу с файлом и т.д. Всё это необходимо учитывать.
  • 2. Непосредственно работа с файлом - запись и чтение. Здесь также нужно помнить, что мы работаем не с памятью с произвольным доступом, а с буферизированным потоком, что добавляет свою специфику.
  • 3. Закрыть файл. Так как файл является внешним по отношению к программе ресурсом, то если его не закрыть, то он продолжит висеть в памяти, возможно, даже после закрытия программы (например, нельзя будет удалить открытый файл или внести изменения и т.п.). Кроме того, иногда необходимо не закрывать, а "переоткрывать" файл для того, чтобы, например, изменить режим доступа.

Кроме того, существует ряд задач, когда нам не нужно обращаться к содержимому файла: переименование, перемещение, копирование и т.д. К сожалению, в стандарте си нет описания функций для этих нужд. Они, безусловно, имеются для каждой из реализаций компилятора. Считывание содержимого каталога (папки, директории) – это тоже обращение к файлу, потому что папка сама по себе является файлом с метаинформацией.

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

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

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

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

Функция fopen открывает файл. Она получает два аргумента – строку с адресом файла и строку с режимом доступа к файлу. Имя файла может быть как абсолютным, так и относительным. fopen возвращает указатель на объект FILE, с помощью которого далее можно осуществлять доступ к файлу.

Например, откроем файл и запишем в него Hello World

Функция fopen сама выделяет память под объект, очистка проводится функцией fclose. Закрывать файл обязательно, самостоятельно он не закроется.

Функция fopen может открывать файл в текстовом или бинарном режиме. По умолчанию используется текстовый. Режим доступа может быть следующим

Параметры доступа к файлу.
Тип Описание
r Чтение. Файл должен существовать.
w Запись нового файла. Если файл с таким именем уже существует, то его содержимое будет потеряно.
a Запись в конец файла. Операции позиционирования (fseek, fsetpos, frewind) игнорируются. Файл создаётся, если не существовал.
r+ Чтение и обновление. Можно как читать, так и писать. Файл должен существовать.
w+ Запись и обновление. Создаётся новый файл. Если файл с таким именем уже существует, то его содержимое будет потеряно. Можно как писать, так и читать.
a+ Запись в конец и обновление. Операции позиционирования работают только для чтения, для записи игнорируются. Если файл не существовал, то будет создан новый.

Если необходимо открыть файл в бинарном режиме, то в конец строки добавляется буква b, например “rb”, “wb”, “ab”, или, для смешанного режима “ab+”, “wb+”, “ab+”. Вместо b можно добавлять букву t, тогда файл будет открываться в текстовом режиме. Это зависит от реализации. В новом стандарте си (2011) буква x означает, что функция fopen должна завершиться с ошибкой, если файл уже существует. Дополним нашу старую программу: заново откроем файл и считаем, что мы туда записали.

Вместо функции fgets можно было использовать fscanf, но нужно помнить, что она может считать строку только до первого пробела.
fscanf(file, "%127s", buffer);

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

Функции fprintf и fscanf отличаются от printf и scanf только тем, что принимают в качестве первого аргумента указатель на FILE, в который они будут выводить или из которого они будут читать данные. Здесь стоит сразу же добавить, что функции printf и scanf могут быть без проблем заменены функциями fprintf и fscanf. В ОС (мы рассматриваем самые распространённые и адекватные операционные системы) существует три стандартных потока: стандартный поток вывода stdout, стандартный поток ввода stdin и стандартный поток вывода ошибок stderr. Они автоматически открываются во время запуска приложения и связаны с консолью. Пример

Ошибка открытия файла

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

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

В простых случаях можно действовать влоб, как в предыдущем куске кода. В более сложных случаях используются методы, подменяющиее RAII из С++: обёртки, или особенности компилятора (cleanup в GCC) и т.п.

Буферизация данных

  • 1) Если он заполнен
  • 2) Если поток закрывается
  • 3) Если мы явно указываем, что необходимо очистить буфер (здесь тоже есть исключения:)).
  • 4) Также очищается, если программа завершилась удачно. Вместе с этим закрываются и все файлы. В случае ошибки выполнения этого может не произойти.

Форсировать выгрузку буфера можно с помощью вызова функции fflush(File *). Рассмотрим два примера – с очисткой и без.

Раскомментируйте вызов fflush. Во время выполнения откройте текстовый файл и посмотрите на поведение.

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

которая принимает уже открытый FILE и указатель на новый буфер. Размер нового буфера должен быть не меньше чем BUFSIZ (к примеру, на текущей рабочей станции BUFSIZ равен 512 байт). Если передать в качестве буфера NULL, то поток станет небуферизированным. Можно также воспользоваться функцией

  • _IOFBF - полная буферизация. Данные записываются в файл, когда он заполняется. На считывание, буфер считается заполненным, когда запрашивается операция ввода и буфер пуст.
  • _IOLBF - линейная буферизация. Данные записываются в файл когда он заполняется, либо когда встречается символ новой строки. На считывание, буфер заполняется до символа новой строки, когда запрашивается операция ввода и буфер пуст.
  • _IONBF – без буферизации. В этом случае параметры size и buffer игнорируются.

Пример: зададим свой буфер и посмотрим, как осуществляется чтение из файла. Пусть файл короткий (что-нибудь, типа Hello, World!), и считываем мы его посимвольно

Видно, что данные уже находятся в буфере. Считывание посимвольно производится уже из буфера.

Функция int feof (FILE * stream); возвращает истину, если конец файла достигнут. Функцию удобно использовать, когда необходимо пройти весь файл от начала до конца. Пусть есть файл с текстовым содержимым text.txt. Считаем посимвольно файл и выведем на экран.

Всё бы ничего, только функция feof работает неправильно. Это связано с тем, что понятие "конец файла" не определено. При использовании feof часто возникает ошибка, когда последние считанные данные выводятся два раза. Это связано с тем, что данные записывается в буфер ввода, последнее считывание происходит с ошибкой и функция возвращает старое считанное значение.

Этот пример сработает с ошибкой (скорее всего) и выведет последний символ файла два раза.

Решение – не использовать feof. Например, хранить общее количество записей или использовать тот факт, что функции fscanf и пр. обычно возвращают число верно считанных и сопоставленных значений.

Примеры

1. В одном файле записаны два числа - размерности массива. Заполним второй файл массивом случайных чисел.

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

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

4. В файле записаны целые числа. Найти максимальное из них. Воспользуемся тем, что функция fscanf возвращает число верно прочитанных и сопоставленных объектов. Каждый раз должно возвращаться число 1.

Другое решение считывать числа, пока не дойдём до конца файла.

5. В файле записаны слова: русское слово, табуляция, английское слово, в несколько рядов. Пользователь вводит английское слово, необходимо вывести русское.

Файл с переводом выглядит примерно так

солнце sun
карандаш pen
шариковая ручка pencil
дверь door
окно windows
стул chair
кресло armchair

и сохранён в кодировке cp866 (OEM 866). При этом важно: последняя пара cлов также заканчивается переводом строки.

Алгоритм следующий - считываем строку из файла, находим в строке знак табуляции, подменяем знак табуляции нулём, копируем русское слово из буфера, копируем английское слово из буфера, проверяем на равенство.

6. Подсчитать количество строк в файле. Будем считывать файл посимвольно, считая количество символов '\n' до тех пор, пока не встретим символ EOF. EOF – это спецсимвол, который указывает на то, что ввод закончен и больше нет данных для чтения. Функция возвращает отрицательное значение в случае ошибки.
ЗАМЕЧАНИЕ: EOF имеет тип int, поэтому нужно использовать int для считывания символов. Кроме того, значение EOF не определено стандартом.

email

Всё ещё не понятно? – пиши вопросы на ящик

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