Работа с файлами

Наконец настала очередь знакомиться с функциями, способными создавать, читать, редактировать файлы. Сказать по чести я сам не использую встроенные средства для работы с файлами. Мне быстрее и удобнее работать с файлами при помощи моей библиотеки Window9 , либо с помощью API функций Windows.

 

OPEN

Для создания , открытия файла в FreeBasic есть функция Open. Она имеет как бы один параметр в котором сочетаются несколько параметров с ключевыми словами, которые разделены пробелами. Никаких разделяющих запятых нет.

Синтаксис:

Open( имя файла, For {Input | Output | Append}, As дескриптор )
 ИЛИ ТАК
Open( имя файла, For Binary,  Access{Read | Write}, As дескриптор )
 ИЛИ ТАК
Open( имя файла, For Random, Access{Read | Write}, As дескриптор [размер буфера] )

  • имя файла с полным путем к нему
  • режим работы с файлом
    • текстовый
      • Input
        • Encoding
          • ASCII
          • UTF-8
          • UTF-16
          • UTF-32
      • Output
        • Encoding
          • ASCII
          • UTF-8
          • UTF-16
          • UTF-32
      • Append
        • Encoding
          • ASCII
          • UTF-8
          • UTF-16
          • UTF-32
    • двоичный
      • Binary
        • Access
          • Read
          • Write
      • Random
        • Access
          • Read
          • Write
  • Дескриптор файла
  • Размер буфера для записи (длина)

Вот такое дерево я соорудил, но давайте разберем каждую запись.

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

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

  • Input - только для чтения (если файл не будет найден, функция вернет код ошибки с помощью функции Err.)
  • Output - только для записи(если файла не существует, то создаст его. Если файл есть, то перезапишет)
  • Append - для записи в конец файла (предыдущая записанная информация в файле сохраняется

Дополнительно можно указать режим кодировки с помощью ключевого слова Encoding и строки с нужной кодировкой для открытия файла: ASCII или Unicode. По умолчанию кодировка ASCII

В двоичном режиме нужно указывать одно из ключевых слов:

  • Binary - Запись или чтение двоичных стандартных типов данных (Byte, Integer и пр.).
  • Random - Запись или чтение двоичных данных нестандартных типов данных

Дополнительно в двоичном режиме можно указать доступ к файлу с помощью ключевого слова Access и:

  • Read - только чтение
  • Write - только запись
    Если ничего не указывать, то по умолчанию и то и другое.

Дескриптор файла необходим для функций позволяющих считывать информацию из файла или записывать ее в файл. Представьте себе, если бы дескриптор каким нибудь образом был не нужен. Тогда при открытии нескольких файлов, как бы вы определяли в какой файл нужно записать , а из которого прочитать? Дескриптор  - это  идентификатор файла, как у человека есть фамилия , имя и отчество.
Записывается дескриптор с помощью символа # и цифры (либо переменной, олицетворяющей эту цифру)

Последний параметр размер буфера для чтения или записи. По умолчанию буфер равен 128 байт.

Во всей записи функции Open не обошлось без вспомогательных команд For и As.

Далее примеры записи команды Open

Пример_1:

'Открытие текстового файла с именем 1.txt для записи
Open "1.txt" For Output As #1
Close #1

Пример_2:

'Открытие текстового файла с именем 1.txt для чтения
Open "1.txt" For Input As #1
Close #1

Пример_3:

'Открытие текстового файла с именем 1.txt для чтения в юникоде
Open "1.txt" For Input Encoding "UTF-8" As #1
Close #1

Пример_4:

'Открытие двоичного файла с именем 1.txt для чтения и записи
Open "1.txt" For Binary As #1
Close #1

Пример_5:

'Открытие двоичного файла с именем 1.txt только для чтения
Open "1.txt" For Binary Access Read As #1
Close #1

Пример_6:

'Открытие двоичного файла с именем 1.txt только для чтения с нестандартным типом данных
Open "1.txt" For Random Access Read As #1 len=200
Close #1

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

 

FreeFile

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

Dim D As Integer = Freefile
Open "File1" For Input As #D
Close

 

Print # и Input #

Для записи в файл или чтения из него, который открыт в текстовом режиме, применяются команды Print # (запись) и Input # (чтение). Помните я говорил о многофункциональности функций Print и Input ? При работе с файлами эти функции используются практически так же как в консоли. Есть конечно отличия и давайте их разберем.
Функция Print при работе с файлами, дополнительно имеет один параметр в начале. Этим параметром является дескриптор файла, тот же что и для функции OPEN. В остальном никаких отличий нет, если конечно принять во внимание что информация выводится не в консоль, а в файл.

Пример_1:

Open "1.txt" For Output As #1
Print #1, "FreeBasic"
Close #1

Пример_2:

Open "1.txt" For Output As #1
Print #1,"" 'пустая строка
Print #1, 10;20;9999 
Print #1, 10,20,9999
Close #1


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

пустая строка
 10 20 9999
 10         20          9999
Наверно ожидалось, что вторая записываемая строка будет сочетать в себе слитное содержание всех трех цифр (10209999) . Однако они разделены пробелом. Связано это с тем, что функция Print при преобразовании числа, автоматом впереди добавляет пробел (в консоли кстати так же). Если нужна запись вместе , то преобразовать придется вручную с помощью строковых функций (Str или с помощью оператора &). 

Функция Input при работе с файлами, вместо строковой подсказки для пользователя в первом параметре имеет дескриптор файла.

Пример:

Dim s As String 
'записываем в файл
Open "1.txt" For Output As #1
Print #1, "FreeBasic"
Close #1
'читаем из файла
Open "1.txt" For Input As #1
Input #1,S
? s
Sleep
Close #1

 

Line Input #

Представьте себе, что нам нужно прочитать строку содержащую запятые. Тогда как вы понимаете, функция Input не годится, поскольку она разделит строку на аргументы. И в итоге вместо "Казнить нельзя, помиловать" , получит только первые два слова.
Тут нас выручит знакомая нам консольная функция Line Input .
Перед тем как запускать пример ниже, нужно сохранить файл с исходным кодом в кодировке Unicode. Для этого в редакторе FbEdit в меню файл->сохранить как ставим галочку напротив Save As Unicode

Dim s As Wstring * 128="Казнить нельзя, помиловать"
'записываем в файл
Open "1.txt" For Output Encoding "utf-8" As #1
Print #1, s
Close #1
'читаем из файла
Open "1.txt" For Input Encoding "utf-8" As #1
Line Input #1,s
? s
Sleep
Close


В результате вы увидели в консоли русские символы. Это один из способов вывода русских символов в консоль, но я предпочитаю лучше преобразовывать с помощью Api функций, хотя бы потому, что большинство Api функций работает со стандартом ASCII. Хотя для некоторых есть прототипы для Unicode.

 

Print # Using

Так же можно применять для записи в файл функцию с расширенным форматированием Print # Using. Использование ее точно такое же как в консоли:

Open "1.txt" For Output As #1
Print #1, Using "This file is called '&'"; "1.txt"
Close #1

 

WRITE #

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

Open "1.txt" For Output As #1
Write #1,10,20,30
Close

 

Get #  и Put #

При работе с двоичными файлами скорость несомненно выше, чем при работе в текстовом режиме. Для двоичного режима уже бесполезно использовать функции текстового режима Print, Input и пр. Конечно можно было разработчикам дополнить возможностями текстовые функции, но тогда скорость считывания и записи была бы на порядок ниже. Возможно именно поэтому для таких целей были определены другие функции Get # (для чтения)  и  Put # (для записи).

Синтаксис этих функций одинаков:

Get дескриптор, позиция в файле, буфер , [размер буфера]

Put дескриптор, позиция в файле, буфер , [размер буфера]

  • Дескриптор , тот же, что и для функции OPEN
  • Позиция в файле с которой начинать читать или записывать. Если не указывать, то берется та позиция, на которой остановилось чтение или запись.
  • Может быть любой буфер памяти: переменная, массив, и пр.
  • Размер буфера (необязательный параметр), особенно полезен для буфера, выделенного вручную, например с помощью функции Allocate.

Пример_1:

Dim a As Integer = 565656
'Записываем в файл
Open "1.txt" For Binary As #1
Put #1, , a
Close #1
'Читаем из файла
Open "1.txt" For Binary As #1
Get #1,,a
? a
Sleep
Close #1

Пример_2

Dim a(5) As Integer = {1,2,3,4,5,6}
'Записываем в файл
Open "1.txt" For Binary As #1
Put #1, , a(0),3
Close #1
'Очищаем массив
Erase (a)
'Читаем из файла
Open "1.txt" For Binary As #1
Get #1,,a(0),3
For b As Integer = 0 To 5
    ? a(b)
Next
Sleep
Close #1


В примере_2  я создал массив с 6 ячейками и заполнил их цифрами от 1 до 6. Далее записал в файл не весь массив, а только первые три ячейки. Дальше очистил массив. Потом прочитал файл, заполняя половину массива. И вывел в цикле в консоль содержимое всего массива.

Пример_3:

'Открытие двоичного файла с именем 1.txt с нестандартным типом данных
Type AA
    a As Byte
    b As Integer
End Type
Dim F As AA= Type(100,599999)
Open "1.txt" For Random As #1 Len = Sizeof(AA)
Put #1,,F
Close
Open "1.txt" For Random As #1 Len = Sizeof(AA)
Dim G As AA
Get #1,,G
Close
? G.a
? G.b
Sleep

 

SEEK

Мы узнали как записывать информацию в начало файла. Но бывает, что нужно записать что-то в середину. Для того, чтобы это сделать есть функция перемещения указателя в файле для чтения и(или) записи. Функция как вы поняли носит название Seek. Она же кстати может получать текущее значение указателя в файле.

Синтаксис:

Для получения указателя:

текущее значение указателя = SEEK( дескриптор)

Для установки указателя:

SEEK  дескриптор ,  новое значение указателя

 

Пример:

Dim i As Integer = 1111, f As Integer = Freefile
Open "1.txt" For Binary As #f
? Seek(f)'указатель на 1 позиции
Put #f,,i
? Seek(f)'указатель на 5 позиции
Seek f,3 ' ставим указатель на 2 позицию
Put #f,,i
Close #f
Open "1.txt" For Binary As #f
Get #f,,i
? i
Close #f
Sleep

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

Open "1.txt" For Output As #1
Print #1, "FreeBasic"
? Seek(1)
Close #1
Sleep


Наверно заметили, что букв в слове FreeBasic  9 , а указатель находится на 12 позиции. Все правильно, ведь в строке дополнительно еще помещаются спецсимволы 13 и 10 ( возврат каретки и перенос строки )

 

LOC

Так же для получения указателя последнего положения при чтении или записи можно использовать функцию LOC. У нее всего один параметр: номер дескриптора файла.

Пример:

Open "1.txt" For Binary As #1
Dim t As Integer = 36474
Put #1, , t
? Loc(1)
Close
Sleep

 

LOF

Для получения длины файла на диске можно использовать функцию LOF, имеющую один параметр: номер дескриптора файла

Пример:

Open "1.txt" For Binary As #1
Dim t As Integer = 36474
Put #1, , t
Close
Open "1.txt" For Binary As #1
? Lof(1)
Close
Sleep

 

EOF

Если функция EOF возвращает ненулевое значение (-1), то это скажет нам , что указатель находится в конце файла. Это часто бывает полезным , например при прочтении всего файла.

Dim s As String
'записываем в файл
Open "1.txt" For Output As #1
Print #1, "FreeBasic"
Print #1, "Assembler"
Close #1
'читаем из файла
Open "1.txt" For Input As #1
While Eof(1)=0
    Input #1,S
    ? s
Wend
Sleep
Close #1

 

LOCK и UNLOCK

Когда несколько программ могут одновременно обращаться к одному файлу, это может вызвать ошибки. Для безопасного чтения и записи в таких случаях лучше применять функции блокировки определенных участков файла. В итоге например нам надо записать что-то в начале файла, середина и конец файла нас не интересует. Мы попросту блокируем нужный участок файла и пишем , остальные участки файла остаются доступными для других программ. Для блокировки и разблокировки можно использовать функции Lock  и  UnLock. У функций одинаковые параметры:

Lock дескриптор , диапазон
UnLock дескриптор , диапазон

Пример:

Dim i As Integer = 33333
Open "1.txt" For Binary As #1
Put #1,,i
Put #1,5,i
Close #1
Open "1.txt" For Binary As #1
Lock #1, 1 To 4
Put #1,1,i
Unlock #1, 1 To 4
Close #1


Все примеры, что мы рассмотрели были по возможности кратки, но почти все были построены не совсем корректно. Представьте себе, что файла для открытия не оказалось, либо система блокирует создание файла, тогда все остальные функции завязанные на этом дескрипторе могут вызвать серьезные аварии в программе. Для того, чтобы избежать подобного, нужно всегда после работы функции OPEN проверять функцией ERR  на возможность ошибок. Если произошла какая-то ошибка, тогда как минимум не использовать функции привязанные к этому дескриптору. Функция ERR возвращает 0 если ошибок не было, в остальных случаях возвращает код ошибки.

Пример:

Open "1.txt" For Output As #1
If Err = 0 Then
   Print #1 ,"FreeBasic"
   Close
Endif

Мы рассмотрели достаточно много, но кое-что еще осталось, например удаление, копирование, перемещение  файла. Это мы оставим на следующую статью.

Всего доброго!

содержание | назад | вперед