Чтение текстовых файлов во встроенном языке 1С 8.3

· 9 мин

Содержание

Прочитать текст из файла в 1С проще всего объектом ЧтениеТекста: Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8), затем Прочитать() для всего содержимого или ПрочитатьСтроку() в цикле для построчной обработки. Если файл выбрал пользователь на своём компьютере, сначала передайте его на сервер через временное хранилище и читайте уже там.

Чаще всего код ломается не на самом чтении, а рядом: файл лежит не на той машине, кодировка не та, файл занят другой программой. Все примеры кода рассчитаны на платформу 8.3 с управляемыми формами и проверяются в обработке за пару минут.

Где выполняется код: клиент или сервер

Перед тем как писать чтение, определите, на какой машине лежит файл. Путь C:\Обмен\prices.txt в серверном коде — это диск сервера 1С, а не компьютер бухгалтера.

Где файлГде читатьЧем
На сервере 1С или в сетевой папке, доступной серверуВ серверной процедуреЧтениеТекста, ТекстовыйДокумент
На компьютере пользователяПередать на сервер, читать тамВременное хранилище + ДвоичныеДанные
Файловая база, всё на одном ПКМожно и на клиенте, и на сервереЛюбой способ

Что учесть при серверном чтении:

  • 🔐 Служба сервера 1С работает от своей учётной записи (по умолчанию USR1CV8). У неё должны быть права на чтение папки, иначе будет ошибка доступа, хотя под вашим пользователем файл открывается.
  • 🌐 Сетевые диски, подключённые буквой у пользователя, служба не видит. Используйте UNC-путь вида \\fileserver\obmen\prices.txt.
  • 🐧 Если сервер 1С на Linux, пути пишутся через /, а регистр букв в именах файлов имеет значение.
  • 🌍 В веб-клиенте прямого доступа к файловой системе нет. Там файл всегда передают на сервер через временное хранилище.

Объект ЧтениеТекста: как прочитать текст из файла

ЧтениеТекста — основной инструмент для текстовых файлов. Он читает файл последовательно и не держит лишнего в памяти.

Параметры конструктора по порядку:

  1. Имя файла или поток.
  2. Кодировка — значение КодировкаТекста или строка, например "windows-1251".
  3. Разделитель строк.
  4. Конвертируемый разделитель строк.
  5. Монопольно — открывать ли файл в монопольном режиме.

Чтение всего файла в одну строку:

&НаСервереБезКонтекста
Функция ПрочитатьФайлЦеликом(ИмяФайла)

    Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8);
    Текст = Чтение.Прочитать();
    Чтение.Закрыть();

    Возврат Текст;

КонецФункции

Метод Прочитать() без параметра возвращает весь оставшийся текст. С параметром — указанное число символов: Чтение.Прочитать(1000) вернёт первые 1000 символов. Это удобно, когда нужно заглянуть в начало файла и понять, тот ли формат пришёл.

Всегда вызывайте Закрыть(). Пока объект не закрыт, файл остаётся занятым: его нельзя удалить или перезаписать, а повторный запуск обработки может упасть с ошибкой доступа.

Если объект создан без имени файла, его открывают отдельно: Чтение.Открыть(ИмяФайла, "windows-1251"). Так удобно читать несколько файлов одним объектом по очереди.

Файл занят другой программой

Типичный случай — лог или выгрузка, которую прямо сейчас пишет внешняя программа. По умолчанию ЧтениеТекста открывает файл монопольно и падает, если файл занят. Передайте пятым параметром Ложь:

Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8, , , Ложь);

Так платформа откроет файл без монопольной блокировки. Учтите: если запись ещё идёт, последняя строка может прийти обрезанной.

Построчное чтение больших файлов

Файл на сотни мегабайт целиком в строку читать не стоит: весь текст ляжет в память рабочего процесса. Читайте по одной строке и сразу обрабатывайте.

ПрочитатьСтроку() возвращает очередную строку без символа перевода строки. Когда файл кончился, метод возвращает Неопределено — это и есть условие выхода из цикла.

&НаСервереБезКонтекста
Процедура ЗагрузитьПрайс(ИмяФайла)

    Чтение = Новый ЧтениеТекста(ИмяФайла, "windows-1251");
    НомерСтроки = 0;

    Строка = Чтение.ПрочитатьСтроку();
    Пока Строка <> Неопределено Цикл

        НомерСтроки = НомерСтроки + 1;

        Если НомерСтроки > 1 И ЗначениеЗаполнено(СокрЛП(Строка)) Тогда
            Поля = СтрРазделить(Строка, ";", Истина);
            Если Поля.Количество() >= 3 Тогда
                Артикул = СокрЛП(Поля[0]);
                Наименование = СокрЛП(Поля[1]);
                Цена = Число(СтрЗаменить(Поля[2], ",", "."));
                // запись в регистр или таблицу значений
            Иначе
                Сообщить(СтрШаблон("Строка %1: мало полей", Формат(НомерСтроки, "ЧГ=0")));
            КонецЕсли;
        КонецЕсли;

        Строка = Чтение.ПрочитатьСтроку();

    КонецЦикла;

    Чтение.Закрыть();

КонецПроцедуры

Что здесь сделано по-взрослому:

  • 📌 Первая строка (заголовки колонок) пропускается по счётчику.
  • 📌 Пустые строки в конце файла не вызывают ошибок.
  • 📌 Номер строки выводится через Формат(..., "ЧГ=0"), иначе на номере 1 500 появится пробел-разделитель групп.
  • 📌 Запятая в цене меняется на точку перед Число().

Если Число() получит строку вроде "1 250,00" с пробелом внутри, будет исключение. Перед преобразованием уберите пробелы: СтрЗаменить(Поля[2], " ", ""). А неразрывный пробел убирают отдельно: СтрЗаменить(Значение, Символы.НПП, "").

Как ускорить загрузку тысяч строк

Само чтение файла быстрое. Тормозит то, что делается с каждой строкой:

  1. Не ищите элемент справочника запросом на каждой строке. Сначала соберите все артикулы в массив, одним запросом получите соответствия, затем ищите в Соответствие.
  2. Записывайте набор записей регистра один раз, а не по строке.
  3. Для очень больших файлов делите загрузку на порции и запускайте её фоновым заданием, чтобы не держать сеанс пользователя.

Кодировки текстового файла: UTF-8, Windows-1251, OEM

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

Откуда файлОбычная кодировкаЧто указать
Современные сервисы, API, выгрузки из LinuxUTF-8КодировкаТекста.UTF8
Старые Windows-программы, Excel «CSV (разделители — запятые)»Windows-1251"windows-1251"
Консольные утилиты Windows, bat-файлыCP866КодировкаТекста.OEM
Блокнот Windows «Юникод»UTF-16 LEКодировкаТекста.UTF16

КодировкаТекста.ANSI означает кодовую страницу операционной системы, на которой выполняется код. На русской Windows это 1251, а на сервере Linux — совсем не обязательно. Поэтому для файлов в 1251 надёжнее писать строку "windows-1251" явно.

BOM и невидимый символ в начале

Файлы UTF-8 иногда начинаются с метки BOM. Если кодировка указана как UTF-8, платформа распознаёт метку. Но если файл прочитан как 1251, в первой строке появятся лишние символы п»ї, и первый заголовок колонки не совпадёт с ожидаемым.

Признак проблемы: сравнение Поля[0] = "Артикул" даёт Ложь, хотя на экране текст одинаковый. Решение — указать правильную кодировку, а не вырезать символы руками.

Если кодировка заранее неизвестна

Прочитайте начало файла в UTF-8 и проверьте, нет ли в нём символа замены � (код 65533). Если есть — перечитайте файл в 1251:

Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8);
Начало = Чтение.Прочитать(4000);
Чтение.Закрыть();

Кодировка = КодировкаТекста.UTF8;
Если Начало <> Неопределено И СтрНайти(Начало, Символ(65533)) > 0 Тогда
    Кодировка = "windows-1251";
КонецЕсли;

Это эвристика, а не гарантия. Если источник файлов один и постоянный, проще договориться о кодировке и зашить её в код.

Чтение из временных файлов и из памяти

Файл с компьютера пользователя на сервер напрямую не попадёт. Схема такая: клиент помещает файл во временное хранилище, сервер достаёт оттуда ДвоичныеДанные и читает их.

Клиентская часть формы:

&НаКлиенте
Процедура ЗагрузитьИзФайла(Команда)
    Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
    НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры

&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
    Если Результат Тогда
        ОбработатьФайлНаСервере(Адрес);
    КонецЕсли;
КонецПроцедуры

На сервере есть три способа прочитать полученные данные.

Способ 1. Сразу в строку — для небольших файлов:

ДД = ПолучитьИзВременногоХранилища(Адрес);
Текст = ПолучитьСтрокуИзДвоичныхДанных(ДД, КодировкаТекста.UTF8);

Способ 2. Через поток — построчно и без временного файла на диске:

ДД = ПолучитьИзВременногоХранилища(Адрес);
Поток = ДД.ОткрытьПотокДляЧтения();
Чтение = Новый ЧтениеТекста(Поток, КодировкаТекста.UTF8);
Строка = Чтение.ПрочитатьСтроку();
Пока Строка <> Неопределено Цикл
    // обработка строки
    Строка = Чтение.ПрочитатьСтроку();
КонецЦикла;
Чтение.Закрыть();
Поток.Закрыть();

Способ 3. Через временный файл — когда дальше нужен именно путь к файлу, например для внешней библиотеки:

ДД = ПолучитьИзВременногоХранилища(Адрес);
ИмяВременного = ПолучитьИмяВременногоФайла("txt");
ДД.Записать(ИмяВременного);
Попытка
    Чтение = Новый ЧтениеТекста(ИмяВременного, КодировкаТекста.UTF8);
    Текст = Чтение.Прочитать();
    Чтение.Закрыть();
Исключение
    УдалитьФайлы(ИмяВременного);
    ВызватьИсключение;
КонецПопытки;
УдалитьФайлы(ИмяВременного);

Удаляйте временные файлы и при ошибке тоже. ПолучитьИмяВременногоФайла только придумывает имя, сам файл платформа не уберёт. На загруженном сервере такие хвосты за месяцы занимают гигабайты в каталоге временных файлов.

Если текст скопирован пользователем в буфер обмена, файл вообще не нужен. Добавьте на форму многострочное поле ввода: пользователь вставит текст через Ctrl+V, а код разберёт его функцией СтрРазделить(Текст, Символы.ПС).

Обработка ошибок при чтении файла

Чтение файла всегда оборачивайте в Попытка ... Исключение. Файл могут удалить, переименовать или открыть в Excel в момент загрузки.

&НаСервереБезКонтекста
Функция ПрочитатьБезопасно(ИмяФайла, Кодировка)

    Файл = Новый Файл(ИмяФайла);
    Если Не Файл.Существует() Тогда
        ВызватьИсключение СтрШаблон("Файл не найден: %1", ИмяФайла);
    КонецЕсли;

    Чтение = Неопределено;
    Попытка
        Чтение = Новый ЧтениеТекста(ИмяФайла, Кодировка);
        Текст = Чтение.Прочитать();
        Чтение.Закрыть();
    Исключение
        Если Чтение <> Неопределено Тогда
            Чтение.Закрыть();
        КонецЕсли;
        ЗаписьЖурналаРегистрации("Загрузка из файла", УровеньЖурналаРегистрации.Ошибка, , ,
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение;
    КонецПопытки;

    Возврат Текст;

КонецФункции

Пустой файл — не ошибка: Прочитать() может вернуть Неопределено вместо пустой строки. Проверяйте результат через ЗначениеЗаполнено(), прежде чем передавать его в СтрРазделить.

Диагностика: выберите симптом

ЧтениеТекста, ТекстовыйДокумент или ДвоичныеДанные: что выбрать

У каждого объекта своя ниша. Выбирайте по размеру файла и по тому, нужен ли доступ к произвольной строке.

ОбъектКогда братьПлюсыМинусы
ЧтениеТекстаБольшие файлы, построчная загрузкаЧитает последовательно, бережёт памятьНельзя сразу перейти к строке 500
ТекстовыйДокументНебольшие файлы, нужен номер строки или показ на формеКоличествоСтрок(), ПолучитьСтроку(N)Весь файл в памяти
ДвоичныеДанные + ПолучитьСтрокуИзДвоичныхДанныхФайл пришёл из временного хранилища, HTTP-ответа, вложенияНе нужен файл на дискеВесь текст в памяти

Пример с ТекстовыйДокумент на сервере:

ТД = Новый ТекстовыйДокумент;
ТД.Прочитать(ИмяФайла, КодировкаТекста.UTF8);
Для НомерСтроки = 1 По ТД.КоличествоСтрок() Цикл
    Строка = ТД.ПолучитьСтроку(НомерСтроки);
    // обработка
КонецЦикла;

Нумерация строк в ТекстовыйДокумент начинается с 1, а не с 0. На клиенте синхронный Прочитать() в современных конфигурациях вызывать нельзя: используйте НачатьЧтение() с описанием оповещения.

Для форматов со структурой отдельные объекты удобнее ручного разбора: ЧтениеXML для XML, ЧтениеJSON для JSON. Разбирать их построчно через ЧтениеТекста — лишняя работа и источник ошибок.

Частые вопросы

Можно ли прочитать файл с компьютера пользователя прямо на сервере?
Нет. Сервер видит только свою файловую систему и сетевые ресурсы, доступные его службе. Файл пользователя передают через временное хранилище.
Как узнать, сколько строк в файле, не читая его целиком?
Без чтения никак. Для больших файлов пройдите по нему ПрочитатьСтроку() и посчитайте строки счётчиком — это не загружает файл в память.
Почему ПрочитатьСтроку возвращает строку с лишним символом в конце?
Файл создан с разделителем строк, который не совпадает с ожидаемым, например только ВК. Укажите нужный разделитель третьим параметром конструктора или удалите символ через СтрЗаменить(Строка, Символы.ВК, "").
Чем читать CSV, где в значениях встречаются точки с запятой в кавычках?
Простой СтрРазделить такие поля разрежет неправильно. Нужен разбор с учётом кавычек: идите по строке посимвольно и переключайте флаг «внутри кавычек».
Работает ли ЧтениеТекста в веб-клиенте?
Нет, на веб-клиенте объект недоступен. Читайте данные на сервере после передачи файла через временное хранилище.