
Запись строки в текстовый файл на VB6: перезапись, дозапись и кодировки
Сохранить строку в файл на VB6 можно тремя способами. Разбираем, чем они отличаются, как дописывать текст без перечитывания файла и как не потерять данные при ошибке.
Задача: сохранить готовую строку на диск
Лог операций, экспорт отчёта в CSV, сохранение настроек, выгрузка результата запроса для другого приложения. Во всех этих сценариях программа сначала собирает текст в строковой переменной, а потом одним вызовом отправляет его в файл. Удобно иметь для этого одну процедуру, которую можно вызывать из любого модуля проекта.
Важная договорённость: процедура пишет строку ровно в том виде, в каком её получила. Переносы строк, разделители полей и заголовки должен расставить вызывающий код, когда готовит текст. Так функция остаётся простой и предсказуемой, а форматированием занимается тот, кто знает, что именно сохраняется.
Второй параметр, без которого не обойтись, это режим: перезаписать файл целиком или дописать текст в конец. Лог почти всегда дописывают, а экспорт обычно создают заново.
Три способа записи в VB6
В классическом Visual Basic текст на диск выводят тремя основными путями, и у каждого своя ниша.
- встроенные операторы Open, Print # и Close: работают без внешних ссылок, быстрые, но пишут только в ANSI-кодировке текущей системы;
- объект FileSystemObject из библиотеки Microsoft Scripting Runtime (scrrun.dll): объектный интерфейс, режимы ForWriting и ForAppending, умеет писать в Unicode (UTF-16);
- объект ADODB.Stream из ADO: единственный штатный способ получить файл в UTF-8, что важно для обмена с вебом и современными системами.
Процедура Str2Text на FileSystemObject
В исходном варианте процедуры для дозаписи файл сначала читался целиком, к старому содержимому приклеивался новый текст, и всё записывалось обратно. Это работает, но на логе размером в десятки мегабайт каждая запись превращается в полное чтение и полную перезапись. У FileSystemObject есть режим ForAppending, который открывает файл и ставит курсор в конец, поэтому перечитывать ничего не нужно.
Третий аргумент OpenTextFile, равный True, разрешает создать файл, если его ещё нет. Это избавляет от отдельной проверки FileExists.
' Запись строки в файл: перезапись или дозапись
Public Sub Str2Text(ByVal strText As String, _
ByVal strPath As String, _
Optional ByVal IsOverWrite As Boolean = True)
Dim fso As New Scripting.FileSystemObject
Dim stm As Scripting.TextStream
Dim mode As Scripting.IOMode
On Error GoTo ErrTrap
mode = IIf(IsOverWrite, ForWriting, ForAppending)
' True: создать файл, если его нет
Set stm = fso.OpenTextFile(strPath, mode, True)
stm.Write strText ' Write не добавляет перевод строки
stm.Close
Exit Sub
ErrTrap:
Dim lngErr As Long, strDesc As String
lngErr = Err.Number: strDesc = Err.Description
If Not stm Is Nothing Then stm.Close ' не оставляем файл открытым
Err.Raise lngErr, "modFiles.Str2Text", strDesc
End SubWrite или WriteLine, Print # или Write #
У TextStream два метода вывода. Write пишет строку как есть, WriteLine добавляет в конец vbCrLf. Если строка уже заканчивается переводом, WriteLine даст лишнюю пустую строку, а при дозаписи без перевода новые данные прилипнут к последней строке файла. Решение простое: выбрать одно правило и соблюдать его везде. Для логов удобно, чтобы каждая запись сама заканчивалась vbCrLf, а процедура использовала Write.
Со встроенными операторами похожая путаница. Print # выводит текст без изменений, а Write # оборачивает строки в кавычки и разделяет значения запятыми, то есть формирует данные для последующего чтения через Input #. Для обычного текстового файла нужен Print #, причём точка с запятой в конце подавляет перевод строки.
Public Sub Str2TextNative(ByVal strText As String, ByVal strPath As String, _
Optional ByVal IsOverWrite As Boolean = True)
Dim f As Integer
f = FreeFile ' свободный номер, а не жёсткий #1
If IsOverWrite Then
Open strPath For Output As #f
Else
Open strPath For Append As #f
End If
Print #f, strText; ' точка с запятой: без vbCrLf в конце
Close #f
End SubКодировки: ANSI, Unicode и UTF-8
Строки в VB6 внутри хранятся в UTF-16, но Open и Print # при записи преобразуют их в кодовую страницу системы. На русской Windows это cp1251, и символы вне неё (например, греческие или китайские) превратятся в знаки вопроса. FileSystemObject по умолчанию ведёт себя так же, но четвёртый параметр OpenTextFile или CreateTextFile со значением TristateTrue включает запись в UTF-16 с маркером BOM.
Если файл будут читать веб-приложение, скрипт на Python или Linux-утилиты, обычно нужен UTF-8. Для этого используют ADODB.Stream: задают Charset, пишут текст методом WriteText и сохраняют через SaveToFile. Учтите, что поток добавит в начало BOM из трёх байтов EF BB BF. Большинство программ это понимают, но некоторые парсеры CSV и старые утилиты воспринимают BOM как часть первого поля.
Public Sub Str2TextUtf8(ByVal strText As String, ByVal strPath As String)
Dim stm As Object
Set stm = CreateObject("ADODB.Stream")
stm.Type = 2 ' adTypeText
stm.Charset = "utf-8"
stm.Open
stm.WriteText strText
stm.SaveToFile strPath, 2 ' adSaveCreateOverWrite
stm.Close
End SubНадёжность: ошибки и атомарная запись
Типичные ошибки при записи: 53 (файл не найден, обычно из-за несуществующей папки), 70 (доступ запрещён или файл открыт другим процессом), 75 (ошибка пути) и 61 (диск переполнен). Процедура должна либо обработать их, либо пробросить наверх с понятным источником, а главное, закрыть файл. Незакрытый дескриптор держит файл заблокированным до завершения программы.
Номер файла всегда берите через FreeFile. Жёсткий #1 ломается, как только две процедуры одновременно работают с файлами, и ошибка 55 (файл уже открыт) появляется в самых неожиданных местах.
Для важных файлов, например настроек, полезен приём атомарной записи: сначала пишем во временный файл рядом с целевым, затем удаляем старый и переименовываем новый через Name. Если программа упадёт в середине записи, на диске останется прежняя целая версия, а не обрезанный файл.
Как это делают сегодня
Вопросы и ответы
Почему русский текст в файле превратился в знаки вопроса?
Файл записан в ANSI на машине с другой кодовой страницей, или в строке есть символы вне cp1251. Пишите в Unicode через FileSystemObject с TristateTrue или в UTF-8 через ADODB.Stream.
Чем Write отличается от WriteLine в TextStream?
Write выводит строку без изменений, WriteLine добавляет vbCrLf в конце. Выберите одно правило для всего проекта, иначе появятся пустые строки или склеенные записи.
Можно ли обойтись без Microsoft Scripting Runtime?
Да. Операторы Open, Print # и Close встроены в язык и не требуют ссылок. Для UTF-8 без FSO подходит ADODB.Stream через позднее связывание CreateObject.