Доступ к файлам на VB

Автор: | 05.02.2018

Введение
Обработка нетипизированных файлов
Обработка текстовых файлов
Обработка типизированных файлов
Хранение картотеки в типизированном файле
Хранение картотеки в нетипизированном файле

Введение

С точки зрения возможностей Visual Basic по их обработке файлы бывают трех типов:

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

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

Обработка нетипизированных файлов

Ниже приведен код программы, обеспечивающей заполнение файла a1.bin значениями массива целых чисел (от 1 до 10, каждое в 4 степени), а затем считывание данных из этого файла в файл a2.bin.

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim FNum1 As Integer
Dim FNum2 As Integer
Dim arrA(10) As Integer
Dim arrB(10) As Integer
Dim intI As Byte
Dim FPath1 As String
Dim FPath2 As String
'Создание каталога C:\TEXT
'MkDir("z:\text")
FPath1 = "z:\text\a1.bin"
FPath2 = "z:\text\a2.bin"
'Определение свободного файлового номера
FNum1 = FreeFile()
'Открытие файла a1.bin
FileOpen(FNum1, FPath1, OpenMode.Binary)
'Определение свободного файлового номера
FNum2 = FreeFile()
'Открытие файла a2.bin
FileOpen(FNum2, FPath2, OpenMode.Binary)
For intI = 1 To 10
'Заполнение массива arrA
arrA(intI) = intI ^ 4
'Заполнение файла a1.bin
FilePut(FNum1, arrA(intI), intI * 4 - 3)
Next
For intI = 1 To 10
'Извлечение из файла a1.bin в массив arrB
FileGet(FNum1, arrB(intI), intI * 4 - 3)
'Заполнение файла a2.bin
FilePut(FNum2, arrB(intI), intI * 4 - 3)
Next
'Закрытие файлов
FileClose(FNum1)
FileClose(FNum2)
'Удаление файлов с диска
'Kill (FPath1)
'Kill (FPath2)
'Удаление пустого каталога TEXT
'RmDir ("z:\text")
End Sub

В программе указываются 2 имени файла:

  • имя файла на диске FPath1 = «z:\text\a1.bin»;
  • имя файла в оперативной памяти FNum1 (идентификатор файла), через него устанавливается связь с файлом при его открытии  FileOpen(FNum1, FPath1, OpenMode.Binary).

Позиция числа при записи в файл FilePut(FNum2, arrB(intI), intI * 4 — 3) и считывании из него FileGet(FNum1, arrB(intI), intI * 4 — 3) определяется  3-м параметром функций.

Если открыть образовавшиеся файлы для просмотра в шестнадцатеричном представлении ( в Windows Commander клавишей F3, затем кнопки меню Options>Hex), то увидим следующее:

На каждое из вводимых в файл  чисел отводится по 4 байта, поскольку тип переменных Integer. Байты  заполняются, начиная слева направо.  Используя калькулятор можно  сопоставить шестнадцатеричные числа из файла с их десятеричными эквивалентами. Например, последние 4  байта (10 27 00  00), если их записать в HEX представлении как 2710, то в десятеричном  представлении (Dec) это будет соответствовать 10000, (10 в 4 степени).

Пример обработки нетипизированного файла см. ссылку (Загрузка и чтение BMP на VB)

Контрольные вопросы:

  • Сформулируйте одним предложением, что делает программа.
  • Сколько байт выделяется под переменную типа Integer?
  • Переменные FPath1 и FNum1 определяют имя файла. В чем между ними различие?
  • Опишите параметры функции FilePut и FileGet.
  • Чем определяется позиция, с которой записывается значение переменной?
  • Определите, какие байты определяют значение 3-го (1, 2, 4, 5 …) числа. Докажите это при помощи калькулятора.

Обработка текстовых файлов

Содержимое текстовых файлов является последовательностью строк произвольной длины. При этом в конце каждой строки находится особое сочетание символов, которое обозначает ее окончание и состоит из «возврата каретки» (код в таблице символов = 13) и «перевода строки» (код=10). В дополнение к сказанному следует заметить, что любой файл, независимо от типа, всегда завершается специальным символом «конец строки.

Ниже приведен код программы, обеспечивающей заполнение текстового файла a.txt несколькими строками. Затем файл закрывается и открывается в режиме чтения. Вместе с этим создается новый файл b.txt и в него копируются все строки из файла a.txt.

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim FNum1 As Integer
Dim FNum2 As Integer
Dim int1 As Integer
Dim int2 As Integer
Dim strA As String
Dim FPath1 As String
Dim FPath2 As String
'Создание каталога C:\TEXT
'MkDir("z:\text")
FPath1 = "z:\text\a.txt"
FPath2 = "z:\text\b.txt"
'Определение свободного файлового номера
FNum1 = FreeFile()
'Открытие файла a.txt
FileOpen(FNum1, FPath1, OpenMode.Output)
strA = "Создан текстовый файл,"
PrintLine(FNum1, strA)
strA = "который состоит "
Print(FNum1, strA)
strA = "из нескольких строк. "
PrintLine(FNum1, strA)
FileClose(FNum1)
FNum1 = FreeFile()
FileOpen(FNum1, FPath1, OpenMode.Input)
FNum2 = FreeFile()
FileOpen(FNum2, FPath2, OpenMode.Append)
'Копирование из a.txt в b.txt
Do Until EOF(FNum1)
strA = LineInput(FNum1)
PrintLine(FNum2, strA)
Loop
'Добавление новой строки в b.txt
strA = "Добавлена новая строка. "
Print(FNum2, strA)
'Определение размеров a.txt и b.txt
int1 = LOF(FNum1) 'Результат: 63
int2 = LOF(FNum2) 'Результат: 88
'Закрытие файлов
FileClose(FNum1)
FileClose(FNum2)
'Удаление файлов с диска
'Kill (FPath1)
'Kill (FPath2)
'Удаление пустого каталога TEXT
'RmDir ("z:\text")
End Sub

В файл b.txt записывается следующая информация

Если просматривать файлы в Hex-коде, то каждый символ описывается одним байтом. При таком способе кодировки можно описать 255 символов. Обратите внимание на наличие  кодов для символов: «возврат каретки» 0D (код 13), «перевод строки» 0A (код 10).

Контрольные вопросы:

  • Сформулируйте одним предложением, что делает программа.
  • Сколько байт выделяется под переменную типа String?
  • Сколько байт выделяется под один символ?
  • Найдите в HEX-листинге и определите назначение  кодов  (D1, E7, 20, oD, 0A)
  • Чем отличаются режимы OpenMode.Output, OpenMode.Input, OpenMode.Append?
  • В чем различие функций PrintLine и Print? Опишите параметры этих функций?
  • Какой результат возвращает функция LOF(FNum1) и в каких единицах измерения?

Обработка типизированных файлов

Информация в типизированных файлах хранится в виде набора одинаковой структуры. Размер одной записи является значением суммы размеров всех её полей.

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

Ниже приведен код программы, обеспечивающей описание структуры для записи Session. После этого создается файл записей a.dat и в него заносятся две одинаковые записи типа Session при помощи переменной rec1. Затем вторая запись извлекается из файла в переменную rec2, после чего в ней изменяются значения полей Name, Number и Progr. Отредактированная запись вновь заносится в файл a.dat.

Private Structure Session
Public Name As String
Public Number As Integer
Public Physics As Byte
Public Math As Byte
Public Progr As Byte
End Structure

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim rec1 As Session
Dim rec2 As Session
Dim FNum As Integer
Dim NRec As Integer
Dim FPath As String

'Заполнение переменной rec1
With rec1
.Name = "Иванов Иван Иванович"
.Number = 123456
.Physics = 4
.Math = 5
.Progr = 3
End With

'MkDir("z:\record")
'ChDir("z:\record") 'не срабатывает
FPath = "z:\a.dat"
FNum = FreeFile()
'Определение размера переменной rec1
'NRec = Len(rec1) не срабатывает, длина String не определена
NRec = 30
'Открытие файла a.dat
FileOpen(FNum, FPath, OpenMode.Random, , , NRec)
'Занесение в файл 2-х одинаковых записей
FilePut(FNum, rec1, 1)
FilePut(FNum, rec1, 2)
'Извлечение 2-й записи в переменную rec2
FileGet(FNum, rec2, 2)

'Редактирование rec2
With rec2
.Name = "Петров Петр Петрович"
.Number = 111111
.Progr = 5
End With

'Занесение измененной записи в файл
FilePut(FNum, rec2, 2)
'Закрытие файлов
FileClose(FNum)
'Удаление файлов с диска
'Kill (FPath)
'Назначение рабочего каталога
'ChDir("c:\program files\Microsoft Visual Studio")
'Удаление пустого каталога
'RmDir ("z:\record")
End Sub

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

Первые два байта (14 00) определяют длину строковой переменной (в Dec-коде 20 символов). После символов следует описание значений других переменных, определенных в структуре Session. Раскодируйте самостоятельно оставшуюся информацию. При этом воспользуйтесь калькулятором для перевода Hex-кода в Dec-код.

Контрольные вопросы:

  • Сформулируйте одним предложением, что делает программа.
  • Сколько байт выделяется под значения полей Name, Number и Progr ? Найдите эти значения в HEX-листинге для записей rec1 и rec2.
  • Опишите параметры функции FileOpen.
  • В HEX-листинге перед начало 2-й записи пустой байт (0о). Как изменить код, чтобы пустого байта не было?
  • Опишите параметры функции FilePut и FileGet.
  • Как экономнее записать число 123456– как целое число или как текстовая переменная?

Хранение картотеки в типизированном файле

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

  • добавление новой карточки в картотеку;
  • просмотр любой карточки по введенному номеру.

Рисунок демонстрирует возможности нашей картотеки.

Выберите команду File > New > Project.

  • В меню Project Types выберите пункт Visual Basic Projects, а в меню Templates – пункт Windows Application.
  • В строке Name введите CardFile в качестве названия проекта, а в строке Location — путь, по которому проект будет располагаться (например, z:\Vb7_Book\ch06).
  • Нажмите кнопку ОК для создания нового проекта.
  • Установите для свойства Text формы Form1 значение Картотека.
  • Разместите на форме Forml два элемента типа GroupBox. Установите свойство Text первого элемента равным «Добавление в картотеку», а второго – «Просмотр картотеки».
  • В группу, образуемую объектом GroupBox1, добавьте четыре элемента типа Label. Установите их свойства в соответствии с таблицей.

  • В ту же самую группу поместите три элемента типа TextBox. В качестве значения свойства Text для каждого элемента укажите пустую строку. Значением свойства Name первого элемента установите строку FirstName, второго – LastName; третьего – Age.
  • Добавьте в первую группу элементов два объекта RadioButton. Установите их свойства в соответствии с таблицей.

  • Расположите в группе одну кнопку типа Button. Установите для свойства Name значение AddButton, а для свойства Text значение Добавить.
  • Теперь добавим элементы во вторую группу (GroupBox2). Разместите в ней восемь элементов типа Label и задайте их свойства в соответствии с таблицей.

  • Добавьте во вторую группу элемент типа TextBox. Установите в качестве значения его свойства Name строку CardNumber, а в качестве значения свойства Text — пустую строку.
  • Поместите в группу кнопку типа Button. Установите для свойства Name значение ShowButton, а для свойства Text значение Показать.

На этом разработка внешнего вида формы заканчивается:

Данные карточки можно хранить, например, в строковой переменной. Если выделить для имени и фамилии человека по 15 символов, для графы «пол» – 1 символ («м» или «ж»), а для возраста 3 символа, то карточка будет представлена в виде строки из 34 символов

Недостающие символы при этом дополняются пробелами. Например, если в поле Имя введена строка Елена, нам придется добавить 10 пробелов, чтобы получить в сумме 15 символов. Теперь можно реализовать функцию добавления новой карточки в картотеку. Двойным щелчком левой кнопки мыши на кнопке AddButton откройте текст процедуры AddButton_Click(). В теле процедуры введите:

Dim card As String
card = FirstName.Text + Space(15 - Len(FirstName.Text))
card += LastName.Text + Space(15 - Len(LastName.Text))
If GenderMale.Checked = True Then card += "м" Else card += "ж"
card += Age.Text + Space(3 - Len(Age.Text))
FileOpen(1, "z:\cardfile.dat", OpenMode.Random, , , 38)
FilePutObject(1, card, 1 + LOF(1) / 38)
FileClose(1)
FirstName.Text = "" 'очищаем поля карточки
LastName.Text = ""
GenderMale.Checked = True
Age.Text = ""

Чтобы дополнить некоторую строку S до длины N, мы использовали выражение

S + Space (N — Len(S))

Стандартная функция Space(K) возвращает строку, состоящую из К пробелов. Если нам требуется получить строку из N символов, ее следует дополнить N — Len(S) пробелами, что и производится в программе. В качестве размера записи в строке

FileOpen(1, "cardfile.dat", OpenMode.Random, , , 38)

выбрано число 38. В документации по VB.NET сказано, что при записи строки в файл процедурой FilePutObject () происходит запись еще четырех байтов, описывающих строку. Так как длина строки, описывающей карточку, составляет 34 байта (15 для имени, 15 для фамилии, 1 для пола и 3 для возраста), суммарная длина записи будет равна 34 + 4 = 38 байтов.

Функция LOF(filenumber) из строки

FilePutObject(1, card, 1 + LOF(1) / 38)

возвращает длину (в байтах) файла с номером filenumber. Разделив полученное значение на размер записи (38 байт), получаем количество записей. Новая же запись, естественно, будет иметь номер, на единицу больший, чем последняя запись картотеки.

После внесения 2-х записей в файл cardfile.dat будет помещена следующая информация:

Каждая запись начинается с 2-х байтов идентификатора (08 00) и длины строковой переменной (22 00). Далее следует описание символов этой переменной.

Нам осталось только реализовать функцию просмотра картотеки. Выберите команду View > Designer, чтобы увидеть главную форму приложения. Двойным щелчком левой кнопки мыши на кнопке ShowButton откройте процедуру ShowButton_Click(). В теле процедуры введите:

Dim card As String = ""
FileOpen(1, "z:\cardfile.dat", OpenMode.Random, , , 38)
FileGetObject(1, card, Val(CardNumber.Text))
FileClose(1)
FirstNameTxt.Text = Mid(card, 1, 15)
LastNameTxt.Text = Mid(card, 16, 15)
If Mid(card, 31, 1) = "м" Then GenderTxt.Text = "мужской" _
Else GenderTxt.Text = "женский"
AgeTxt.Text = Mid(card, 32, 3)

 

Функция Mid(string, i, length) возвращает подстроку строки string, начинающуюся с i-ro символа строки string и содержащую length символов. Таким образом, подстрока Mid(card, 1,15) представляет из себя значение поля Имя, подстрока Mid(card, 16,15) соответствует полю Фамилия, Mid(card, 31,1) – это пол, a Mid(card, 32,3) – возраст.

Как уже говорилось выше, типизированные файлы состоят из набора однотипных записей. Мы можем прочитать любую запись, изменить произвольную запись в файле или добавить новую запись в конец файла. Как вы уже знаете, прежде всего, файл нужно открыть при помощи процедуры FileOpen(). Для открытия типизированного файла в качестве режима работы (workmode) необходимо указать OpenMode.Random (и для чтения, и для записи). Кроме того, следует задать значение необязательного параметра RecordLength:

FileOpen(filenumber, filename, workmode. . . RecordLength)

RecordLength — это размер записи в байтах, из которых состоит файл. При работе с типизированными файлами для чтения и записи данных используются процедуры FileGetObject() и FilePutObject. Их синтаксис выглядит так:

FileGetObject (filenumber, var, recnumber)
Fi1ePutObject (filenumber, var, recnumber)

Процедура FileGetObject ( )считывает в переменную var запись с номером recnumber из входного файла filenumber (первая запись имеет номер 1, вторая – 2 и т. д.).

Процедура FilePutObject ( ), наоборот, записывает в файл значение переменной var. Если параметр recnumber не указан, то операция будет производиться с текущей записью (если мы работали с первой записью, то текущей будет вторая; если со второй, то третья и т. д.)

Заметьте, для каждого режима работы с файлом существуют свои процедуры и функции чтения/записи. Если попытаться, к примеру, прочесть данные из типизированного файла при помощи функции LineInput, то произойдет ошибка времени выполнения Bad file mode. Как и прежде, закрытие файла производится при помощи процедуры

FileClose(filenumber)

На этом разработка приложения заканчивается.

Контрольные вопросы:

  • Сформулируйте одним предложением, что делает программа.
  • Прочитайте, используя терминологию программиста выражение: card = FirstName.Text + Space(15 — Len(FirstName.Text)). Начните: переменной card присваивается ….
  • Определите, с какой позиции (в байтах)  запишет функция FilePutObject(1, card, 1 + LOF(1) / 38) вторую запись.
  • Что означают байты (08 00) в файле?
  • Опишите назначение и параметры функции FileGetObject(1, card, Val(CardNumber.Text)). Как определяется, сколько символов считывается в переменную card?
  • Прочитайте выражение:LastNameTxt.Text = Mid(card, 16, 15)

Хранение картотеки в нетипизированном файле

Наша картотека обладает одной особенностью: каждая карточка, хранимая в типизированном файле, занимает 38 байт независимо от реального размера. Такой способ хранения нельзя назвать экономным – в итоге существенная часть файла cardfile.dat может расходоваться впустую. Зато доступ к любой записи осуществляется очень быстро, поскольку все записи имеют одинаковый размер и компьютер может легко вычислить, в каком месте файла какая запись находится. Если же время доступа для нас не имеет большого значения, а место на диске расходовать понапрасну не хотелось бы, можно воспользоваться не типизированным файлом. При этом, однако, чтобы прочесть какую-либо запись, нам придется заодно прочесть все предшествующие записи.

Перепишем теперь программу работы с картотекой с использованием не типизированных файлов. Все интерфейсные элементы, конечно, останутся без изменений; необходимо лишь переписать две процедуры – AddButton_Click() и ShowButton_Click(). Измененные версии процедур представлены в листингах 1 и 2.

Листинг 1. Модифицированная процедура AddButton_Click()

Dim gender As String
If GenderMale.Checked = True Then gender = "м" Else gender = "ж"
FileOpen(1, "z:\cardfile.dat", OpenMode.Binary)
FilePutObject(1, FirstName.Text, 1 + LOF(1))
FilePutObject(1, LastName.Text, 1 + LOF(1))
FilePutObject(1, gender, 1 + LOF(1))
FilePutObject(1, Age.Text, 1 + LOF(1))
FileClose(1)
FirstName.Text = ""
LastName.Text = ""
GenderMale.Checked = True
Age.Text = ""

Листинг 2. Модифицированная процедура ShowButton_Click()

Dim i As Integer
Dim FName, LName, Gender, pAge As String
FileOpen(1, "z:\cardfile.dat", OpenMode.Binary)
For i = 1 To Val(CardNumber.Text)
FileGetObject(1, FName)
FileGetObject(1, LName)
FileGetObject(1, Gender)
FileGetObject(1, pAge)
Next i
FileClose(1)
FirstNameTxt.Text = FName
LastNameTxt.Text = LName
If Gender = "м" Then GenderTxt.Text = "мужской" _
Else GenderTxt.Text = "женский"
AgeTxt.Text = pAge

После внесения 2-х записей в файл cardfile.dat будет помешена следующая информация:

Контрольные вопросы:

  • Сформулируйте отличия этой программы от предыдущей.
  • Опишите, каким образом определяется позиция (в байтах), с которой  функция FilePutObject(1, LastName.Text, 1 + LOF(1)) запишет значение  LastName.Text.
  • Опишите, каким образом определяется позиция (в байтах), с которой  функция FileGetObject(1, LName) считывает байты. Как определяется, сколько байтов будет считываться в переменную LName.

Автор: Николай Свирневский

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *