Нетипизированные файлы
Философия. Предположим, перед вами файл, о котором вы ничего не знаете. Вы даже не знаете, текст там записан или музыка, картинки иди видео, а может быть программа? Но вам хочется все-таки прочесть, что в нем есть. Вы пробуете открыть его в Блокноте, но видите только мешанину причудливых символов. Значит это не текстовый файл. Может быть это типизированный файл? Но чтобы его прочесть, нужно знать структуру записи, а вы не знаете. Может быть это картинка? Вы пробуете открыть его в графическом редакторе (20.9.2) – не получается. Может быть, это музыка или видео? Вы пробуете открыть его в нашем Плеере (3.10) – тоже не получается. Что же это? Вопрос не для новичка. Что-то может подсказать вам расширение. Если у вас есть опыт, то что-то может подсказать Блокнот или так называемый двоичный редактор.
Что мы знаем о файле, о котором ничего не знаем? Только то, что это длинная цепочка байтов, в которых создателем файла закодирована какая-то информация. Какая именно? – вот в чем проблема.
Раз так, то поставим задачу прочесть подряд все байты из файла.
Что такое байт? Байт – это цепочка из 8 битов. Каждый бит – это элементарная компьютерная единичка информации, принимающая одно из двух значений. Принято условно обозначать эти значения, как 0 и 1. Таким образом, байт может иметь такой вид:
11000110
или, скажем, такой:
00000011
Попробуйте догадаться, сколько всего возможно различных сочетаний нулей и единиц в байте. Правильно – 28, то есть 256. Поэтому байт может принимать одно из 256 значений, а значит может закодировать одну из 256 вещей. Природа этих вещей может быть совершенно любая.
Попробуем поглядеть на код любого конкретного байта в файле. Пусть это будет
00100011
Можно ли, глядя на него, догадаться, что создатель файла закодировал им? Совершенно невозможно. Если он записывал целые числа типа Byte, то это число 35. Если он записывал строки или символы, то это половинка от какого-то символа в кодировке Unicode или целый символ # в кодировке ASCII. Если это картинки, музыка или видео, то для того, чтобы сказать, какой смысл имеет каждый отдельный байт файла, нужно знать применяемый в данном файле принцип кодировки картинок, музыки или видео. А это непростая задача – нужно хорошо разбираться в графике, музыке или видео. К тому же принципов кодировок много и они сложны.
К счастью, для того, чтобы посмотреть (или даже подредактировать) картинку и послушать (или подредактировать) музыку, совсем не обязательно разбираться в их кодировании в компьютере. Для этого есть достаточно простые и удобные программы.
Интересно, что про любой незнакомый файл можно вообразить, что это типизированный файл чисел типа Byte или символов в кодировке ASCII. И действительно, взяв любой незнакомый файл, мы можем объявить переменную
Dim а As Byte
и затем применить к файлу в цикле фрагмент
FileGet(1, а)
Debug.WriteLine(а)
в результате чего получим содержимое любого незнакомого файла, как цепочку чисел. Аналогично действуя, можно распечатать тот же самый файл, как цепочку символов. Спрашивается: что же хранится в этом файле – числа или символы? Вполне может быть, что ни то, ни другое, а что-то третье. Нужно только, чтобы кто-то нам сказал, что именно, и рассекретил принцип кодирования. После этого вся информация у нас в руках.
Если про файл известно, что он не является текстовым или типизированным, его называют нетипизированным.
Что мы выяснили? Мы выяснили, что вид файла (текстовый он, типизированный или нетипизированный) не является свойством самого файла, а скорее является точкой зрения на него. Вернее, для нас вид файла определяется тем, какие методы мы с помощью VB к нему применяем. Так, для только что упомянутого незнакомого файла мы вполне можем применить метод StreamReader и получить какой-то результат, тогда для нас данный файл будет текстовым, а не типизированным.
Нетипизированный файл вовсе не означает обязательно незнакомый файл. Очень часто выгодно работать с файлом именно как с нетипизированным, о чем свидетельствует пример о сейсмодатчике, приведенный немного ниже. Поэтому рассмотрим практику работы с нетипизированными файлами. Поскольку нетипизированные файлы обычно не несут в себе регулярной структуры, то основой работы с ними волей-неволей является запись и считывание байтов. До байтов мы вполне можем добраться и при помощи техники работы с типизированными файлами, как я только что упоминал, но лучше для этого применять специальные методы работы с нетипизированными файлами, которые мы сейчас и рассмотрим.
FileStream. Вернемся к современной модели доступа к файлам, так как доступ в ней к нетипизированным файлам достаточно прост. Для него нам понадобится класс FileStream. Этот класс широко применяется для организации самого разнообразного доступа к файлам всех типов. Его конструктор позволяет подробно настроить этот доступ (например, только для чтения, то есть без права записи).
Нетипизированные файлы, как и типизированные, являются файлами с произвольным доступом. Прыжки магнитной головки по файлу к нужному байту осуществляются при помощи метода Seek класса FileStream. Для непосредственной записи и чтения в нетипизированном файле нам понадобятся классы BinaryWriter и BinaryReader.
Записываем в нетипизированный файл массив байтов. Вот процедура для такой записи:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim FS As New FileStream("E:\Папка\Файл.txt", FileMode.OpenOrCreate)
Dim BW As New BinaryWriter(FS)
Dim B() As Byte = {190, 47, 35, 205, 219}
BW.Write(B)
BW.Close()
FS.Close()
End Sub
Пояснения: Сначала мы создаем объект FS класса FileStream. Он создается над файлом E:\Папка\Файл.txt. Второй параметр конструктора указывает, что этот файл можно открывать (Open), а в случае его отсутствия создавать (Create). Затем на основе объекта FS создается объект BW класса BinaryWriter для непосредственной записи в файл. Далее мы объявляем массив B типа Byte из 5 элементов. Затем при помощи метода Write класса BinaryWriter одним оператором записываем массив в файл. Там он располагается в цепочке из 5 байтов. После записи оба объекта закрываются.
Считываем из нетипизированного файла отдельные байты. Пусть байты в файле пронумерованы с 0. Тогда вот процедура считывания 4-го, а затем 2-го байта:
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
Dim FS As New FileStream("E:\Папка\Файл.txt", FileMode.Open)
Dim BR As New BinaryReader(FS)
Dim By As Byte
FS.Seek(4, SeekOrigin.Begin) : By = BR.ReadByte : Debug.WriteLine(By) : Debug.WriteLine(Chr(By))
FS.Seek(2, SeekOrigin.Begin) : By = BR.ReadByte : Debug.WriteLine(By) : Debug.WriteLine(Chr(By))
BR.Close()
FS.Close()
End Sub
Пусть файл создан и записан предыдущей процедурой (вот цепочка байтов: {190, 47, 35, 205, 219}). Тогда данная процедура распечатает вот что:
219
Ы
35
#
Пояснения: Второй параметр конструктора объекта FS указывает, что этот файл можно только открывать (Open). На основе объекта FS создается объект BR класса BinaryReader для чтения из файла. Далее мы объявляем переменную By типа Byte, в которую и будем считывать байты.
Затем работает метод Seek
класса FileStream. Он ставит считывающую головку на 4?й байт, если отсчитывать байты с начала файла, начиная с 0. То, что отсчитывать нужно именно с начала файла, указывает элемент Begin перечисления SeekOrigin.
Раз считывающая головка стоит на нужном байте, его можно прочесть. Делает это метод ReadByte класса BinaryReader. Считанный байт присваивается переменной By. Поскольку эта переменная имеет числовой тип Byte, следующий оператор распечатывает считанный байт как число. Следующий оператор распечатывает этот же байт, но уже в «маске» символа ASCII.
То же самое делает следующая четверка операторов, но уже не с 4-м, а со 2-м байтом файла.
После считывания оба объекта закрываются.
Записываем в нетипизированный файл отдельный байт. Пусть байты в файле пронумерованы с 0. Тогда вот процедура для записи во 2-й байт числа 39:
Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
Dim FS As New FileStream("E:\Папка\Файл.txt", FileMode.OpenOrCreate)
Dim BW As New BinaryWriter(FS)
Dim By As Byte = 39
FS.Seek(2, SeekOrigin.Begin)
BW.Write(By)
BW.Close()
FS.Close()
End Sub