
Чтение текстового файла в строку на VB6: Open, Binary, FSO и UTF-8
Загрузить файл целиком в String нужно для разбора конфигов, шаблонов и логов. В VB6 для этого есть несколько способов, и они заметно различаются скоростью, кодировками и поведением на краях.
Когда файл нужен целиком
Построчное чтение хорошо, когда файл огромный и обрабатывается потоком. Но во многих задачах удобнее получить весь текст одной строкой: подставить значения в HTML-шаблон, найти фрагмент регулярным выражением, сравнить содержимое с эталоном, показать файл в TextBox. Для файлов размером в единицы мегабайт это и проще, и быстрее.
В VB6 есть три инструмента для такой задачи: встроенные операторы Open, Input и Get, объектная библиотека Microsoft Scripting Runtime с классом FileSystemObject и компонент ADODB.Stream из ADO. Разберём каждый и посмотрим, когда что выбирать.
Open For Input и функция Input
Самый старый способ работает ещё со времён QuickBASIC. Функция FreeFile выдаёт свободный номер файла, Open открывает файл под этим номером, LOF возвращает длину, а Input читает нужное количество символов. Тот же номер потом передаётся в Close.
Обратите внимание на структуру обработки ошибок: метка обработчика стоит перед Close, поэтому файл закрывается и при успехе, и при сбое. Close с номером, который не был открыт, ошибку не вызывает, так что код безопасен, даже если сбой случился до Open.
Public Function ReadAllText(ByVal strPath As String) As String
Dim intFile As Integer
On Error GoTo ErrTrap
If Len(Dir$(strPath)) = 0 Then Exit Function ' файла нет
intFile = FreeFile
Open strPath For Input As #intFile
ReadAllText = Input$(LOF(intFile), #intFile)
ErrTrap:
Close #intFile ' закрываем в любом случае
If Err.Number <> 0 Then
Err.Raise Err.Number, "ReadAllText", Err.Description
End If
End FunctionРежим Binary: быстро и без сюрпризов
В режиме Binary VB6 не интерпретирует содержимое. Мы заранее создаём строку нужной длины через Space$ и одним оператором Get заполняем её байтами файла. Это самый быстрый способ в чистом VB6: одна операция чтения вместо разбора по символам.
Байты при этом переводятся в Unicode-строку по текущей кодовой странице системы (для русской Windows это windows-1251). Для ANSI-файлов результат правильный, для UTF-8 кириллица превратится в набор знаков, о чём ниже.
Public Function ReadAllBinary(ByVal strPath As String) As String
Dim intFile As Integer, strBuf As String
On Error GoTo ErrTrap
intFile = FreeFile
Open strPath For Binary Access Read As #intFile
If LOF(intFile) > 0 Then
strBuf = Space$(LOF(intFile)) ' буфер на весь файл
Get #intFile, , strBuf ' одно чтение
End If
ReadAllBinary = strBuf
ErrTrap:
Close #intFile
If Err.Number <> 0 Then Err.Raise Err.Number, "ReadAllBinary", Err.Description
End FunctionFileSystemObject: ReadAll вместо цикла
Для FileSystemObject нужна ссылка Project, References, Microsoft Scripting Runtime (scrrun.dll). Метод OpenTextFile возвращает объект TextStream, а у него есть ReadAll, который отдаёт всё содержимое сразу.
Частая ошибка в чужом коде: чтение циклом Do Until ts.AtEndOfStream с ReadLine и склейкой строк. ReadLine отрезает перевод строки, поэтому на выходе весь файл слипается в одну линию. Если цикл действительно нужен, добавляйте vbCrLf вручную, но проще сразу вызвать ReadAll. Единственная ловушка ReadAll: на пустом файле он выбрасывает ошибку, поэтому сначала проверяем размер.
' Требуется ссылка: Microsoft Scripting Runtime
Public Function ReadAllFso(ByVal strPath As String, _
Optional ByVal blnUnicode As Boolean = False) As String
Dim fso As New FileSystemObject
Dim ts As TextStream
If Not fso.FileExists(strPath) Then Exit Function
If fso.GetFile(strPath).Size = 0 Then Exit Function ' ReadAll упадёт
' TristateTrue читает UTF-16, TristateFalse ANSI
Set ts = fso.OpenTextFile(strPath, ForReading, False, _
IIf(blnUnicode, TristateTrue, TristateFalse))
ReadAllFso = ts.ReadAll
ts.Close
End FunctionФайлы в UTF-8: ADODB.Stream
Ни Open, ни FileSystemObject не умеют читать UTF-8: первый понимает только ANSI, второй ANSI и UTF-16. А современные конфиги, выгрузки из веба и файлы из Git почти всегда в UTF-8. Выручает ADODB.Stream, который есть на любой Windows с установленным MDAC. Ему можно указать кодировку через свойство Charset, и он сам пропустит метку BOM в начале файла.
Подключать ADO ссылкой не обязательно, достаточно позднего связывания через CreateObject. Константа adTypeText равна 2.
Public Function ReadAllUtf8(ByVal strPath As String) As String
Dim stm As Object
Set stm = CreateObject("ADODB.Stream")
stm.Type = 2 ' adTypeText
stm.Charset = "utf-8" ' можно и "windows-1251", "koi8-r"
stm.Open
stm.LoadFromFile strPath
ReadAllUtf8 = stm.ReadText
stm.Close
End FunctionПоиск по содержимому через RegExp
Раз файл уже в строке, искать в нём удобно регулярными выражениями. В VB6 их даёт библиотека Microsoft VBScript Regular Expressions 5.5. Ниже функция, которая возвращает все совпадения шаблона, каждое с новой строки.
Шаблон стоит проверять до вызова: некорректное выражение вызовет ошибку 5017, а неудачный шаблон с вложенными квантификаторами на большом файле может работать очень долго.
' Требуется ссылка: Microsoft VBScript Regular Expressions 5.5
Public Function FindInText(ByVal strText As String, ByVal strPattern As String, _
Optional ByVal blnIgnoreCase As Boolean = True) As String
Dim rx As New RegExp, m As Match, strOut As String
rx.Pattern = strPattern
rx.Global = True ' все совпадения, а не первое
rx.MultiLine = True ' ^ и $ работают по строкам
rx.IgnoreCase = blnIgnoreCase
For Each m In rx.Execute(strText)
strOut = strOut & m.Value & vbCrLf
Next
FindInText = strOut
End Function
' Пример: MsgBox FindInText(ReadAllBinary("C:\app\settings.ini"), "^timeout=.*$")Что выбрать
Короткая шпаргалка по способам из статьи:
- ANSI-файл, нужна скорость и отсутствие зависимостей: Open For Binary и Get;
- проект уже использует FileSystemObject или нужен UTF-16: TextStream.ReadAll с проверкой на пустой файл;
- файл в UTF-8 или другой кодировке, отличной от системной: ADODB.Stream с Charset;
- Open For Input оставьте для заведомо чистых текстовых файлов, в которых не встретится Chr(26).
Как это делают сегодня
Вопросы и ответы
Какую ссылку подключить для FileSystemObject?
Project, References, Microsoft Scripting Runtime (файл scrrun.dll). Без неё типы FileSystemObject и TextStream не будут известны компилятору. Альтернатива: позднее связывание через CreateObject("Scripting.FileSystemObject").
Почему вместо кириллицы получаются кракозябры?
Файл, скорее всего, в UTF-8, а Open и FileSystemObject читают его в системной кодовой странице. Используйте ADODB.Stream с Charset = "utf-8".
Почему ReadAll падает с ошибкой 62?
Так TextStream реагирует на пустой файл. Перед чтением проверьте размер через fso.GetFile(path).Size и возвращайте пустую строку, если он равен нулю.