Создать файл если не существует c
Для удобства обращения информация в запоминающих устройствах хранится в виде файлов.
Файл – именованная область внешней памяти, выделенная для хранения массива данных. Данные, содержащиеся в файлах, имеют самый разнообразный характер: программы на алгоритмическом или машинном языке; исходные данные для работы программ или результаты выполнения программ; произвольные тексты; графические изображения и т. п.
Каталог ( папка , директория ) – именованная совокупность байтов на носителе информации, содержащая название подкаталогов и файлов, используется в файловой системе для упрощения организации файлов.
Файловой системой называется функциональная часть операционной системы, обеспечивающая выполнение операций над файлами. Примерами файловых систем являются 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.
Р абота с текстовым файлом похожа работу с консолью: с помощью функций форматированного ввода мы сохраняем данные в файл, с помощью функций форматированного вывода считываем данные из файла. Есть множество нюансов, которые мы позже рассмотрим. Основные операции, которые необходимо проделать, это
- 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 не определено стандартом.
самый простой способ, который я мог найти до сих пор:
используя File.Create оставит файл открытым, что, вероятно, не то, что вы хотите.
вы можете использовать:
это выглядит немного странно, заметь. Вместо этого вы можете использовать фигурные скобки:
или просто позвоните Dispose напрямую:
в любом случае, если вы собираетесь использовать это более чем в одном месте, вы, вероятно, должны рассмотреть возможность его упаковки в вспомогательный метод, например
обратите внимание, что вызов Dispose непосредственно вместо использования using заявление на самом деле не имеет большого значения здесь, насколько я могу сказать - единственный способ это мог бы разница заключается в том, что поток был прерван между вызовом File.Create , а вызов Dispose . Если это расовое состояние существует, я подозреваю, что это будет и на using версия, если поток был прерван в самом конце File.Create метод, непосредственно перед возвращением значения.
как указывали другие, вы должны избавиться от этого объекта или обернуть его в пустой оператор using.
вы можете связать методы с возвращаемым объектом, поэтому вы можете немедленно закрыть файл, который вы только что открыли в одном операторе.
несколько распространенным случаем использования для создания пустого файла является запуск чего-то другого, происходящего в другом процессе в отсутствие более сложной связи в процессе. В этом случае создание файла может быть атомарным с точки зрения внешнего мира (особенно если запускаемая вещь собирается удалить файл, чтобы "потреблять" триггер).
таким образом, это может помочь создать нежелательное имя (Guid.Метод newguid.ToString()) в том же каталоге, что и файл, который вы хотите создать, а затем сделать файл.Перейдите от временного имени к желаемому имени. В противном случае срабатывает код, который проверяет наличие файла, а затем удаляет триггер может работать в условиях гонки, где файл удаляется до его полного закрытия.
наличие временного файла в том же каталоге (и файловой системе) дает вам атомарность, которую вы можете захотеть. Это дает что-то вроде.
чтобы избежать случайной перезаписи существующего файла использовать:
. и обрабатывать исключение IOException, которое произойдет, если файл уже существует.
File.Create , который предлагается в других ответах, перезапишет содержимое, если файл уже существует. В простых случаях вы можете смягчить это, используя File.Exists() . Однако что-то более надежное необходимо в сценариях, где несколько потоков и / или процессов пытаются создать файлы в одной папке одновременно.
путь.GetTempFileName() создает uniquly назвал пустой файл и возвращает путь к нему.
Если вы хотите управлять путем, но получить случайное имя файла, вы можете использовать GetRandomFileName, чтобы просто вернуть строку имени файла и использовать ее с Create
Как определить, есть ли файл на диске?
Библиотечная функция access позволяет определять режим доступа к файлу, а если второй параметр mode равен нулю, то определяется только существование файла.
Хотя эта функция и не входит в стандарт C/C++, тем не менее, она присутствует в компиляторах Visual C++, Borland C++, Watcom C++ и многих других в неизменном виде.
Способ 2 (_findfirst)
Функция _findfirst возвращает информацию о первом файле, удовлетворяющем заданной маске поиска. Если указать точное имя файла, то мы сможем ответить на наш вопрос.
С помощью этого способа можно определять не только существование отдельного файла, но также и группы файлов, соответствующей заданной маске. А если задать маску как "*.*" , то можно узнать есть ли файлы в заданной директории.
Способ 3 (GetFileAttributes)
Функция GetFileAttributes Win32 API возвращает атрибуты для заданного файла или каталога. В случае ошибки возвращается значение 0xFFFFFFFF.
Этот способ используется во многих примерах из MSDN, что позволяет предположить, что это штатный способ для решения нашей задачи в Win API. Кроме того, это самый быстрый из приведенных здесь способов.
Способ 4 (FindFirstFile)
Этот способ аналогичен способу 2 с той лишь разницей, что для достижения результата используется функция Win32 API.
Способ 5 (MFC)
MFC содержит класс-обёртку для функций Find. API. Мы вполне можем использовать этот класс.
Способ 6 (WTL)
Среди прочих классов, подобных MFC, WTL также содержит и CFindFile .Следовательно, этот способ внешне ни чем не отличается от предыдущего, кроме того, что не требует MFC.DLL. На самом деле этот способ намного быстрее предыдущего. Дело в том, что все функции класса CFindFile являются inline ,так что код, генерируемый компилятором, почти целиком совпадает с кодом для способа 4.
Способ 7 (PathFileExists)
Ещё один способ из предложенных Александром Шаргиным - использование SHLWAPI Path API .
Правда у этого способа имеются определённые недостатки, которые значительно сужают его практическое применение:
- Он не будет работать, если на компьютере не установлен Internet Explorer 4.0 или выше, что может быть вполне вероятно на компьютерах с ранними версиями Windows 95 и Window NT 4.0.
- Функция PathFileExists() не поддерживает UNC имена файлов.
Способ 8 (CreateFile)
Самый очевидный и самый громоздкий способ.
Способ 9 (Pure C++ метод std::ifstream, ::ifstream)
Данный метод состоит в создании временного объекта класса ifstream .Если файл с указанным именем не существует то operator void *() этого класса возвращает NULL pointer - иначе возвращается указатель на сам созданный объект ( this ). Это значение проверяется на NULL pointer - и . все.
. вернее почти все =)
В данном коде ifstream это typedef basic_ifstream < char , char_traits < char >> ifstream; если же Вы пользуетесь старыми заголовочными файлами (с расширением .h) - то для Вас ifstream - это никакой не typedef - а самый настоящий класс. И все было бы прекрасно - если бы не одно но - в этом случае конструктор с именем файла в качестве параметра СОЗДАСТ файл (если он не существует) и в любом случае, проверка на существование файла даст положительный результат. Дело в том, что для "старого" ifstream 'а надо явно указывать что НЕ надо создавать файл через добавление флага ios:: nocreate во втором параметре конструктора. А вот и сам код для такого случая:
Данный метод хорош тем что он 100% портабелен - то есть используются только возможности самого языка С++ (в лице его стандартной библиотеки - которая является его частью).
Способ 11 (Script)
Ни один из перечисленных способов не будет работать из .html документа. Зато из скрипта доступен Scripting.FileSystemObject и нам этого достаточно.
Мы вполне можем использовать Scripting.FileSystemObject и в COM-модуле:
Фактически, это очень извращенный способ вызова все той же функции access() из способа 1, с той разницей, что FileSystemObject работает с именами файлов в UNICODE и под WindowsNT/2k передает имя файла напрямую, а под Windows 9x/Me (и даже 3.1 с интернет эксплорером!) сам преобразовывает его в ANSI.
Читайте также: