Чтение текстовых файлов во встроенном языке 1С 8.3
Содержание
Прочитать текст из файла в 1С проще всего объектом ЧтениеТекста: Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8), затем Прочитать() для всего содержимого или ПрочитатьСтроку() в цикле для построчной обработки. Если файл выбрал пользователь на своём компьютере, сначала передайте его на сервер через временное хранилище и читайте уже там.
Чаще всего код ломается не на самом чтении, а рядом: файл лежит не на той машине, кодировка не та, файл занят другой программой. Все примеры кода рассчитаны на платформу 8.3 с управляемыми формами и проверяются в обработке за пару минут.
Где выполняется код: клиент или сервер
Перед тем как писать чтение, определите, на какой машине лежит файл. Путь C:\Обмен\prices.txt в серверном коде — это диск сервера 1С, а не компьютер бухгалтера.
| Где файл | Где читать | Чем |
|---|---|---|
| На сервере 1С или в сетевой папке, доступной серверу | В серверной процедуре | ЧтениеТекста, ТекстовыйДокумент |
| На компьютере пользователя | Передать на сервер, читать там | Временное хранилище + ДвоичныеДанные |
| Файловая база, всё на одном ПК | Можно и на клиенте, и на сервере | Любой способ |
Что учесть при серверном чтении:
- 🔐 Служба сервера 1С работает от своей учётной записи (по умолчанию
USR1CV8). У неё должны быть права на чтение папки, иначе будет ошибка доступа, хотя под вашим пользователем файл открывается. - 🌐 Сетевые диски, подключённые буквой у пользователя, служба не видит. Используйте UNC-путь вида
\\fileserver\obmen\prices.txt. - 🐧 Если сервер 1С на Linux, пути пишутся через
/, а регистр букв в именах файлов имеет значение. - 🌍 В веб-клиенте прямого доступа к файловой системе нет. Там файл всегда передают на сервер через временное хранилище.
Объект ЧтениеТекста: как прочитать текст из файла
ЧтениеТекста — основной инструмент для текстовых файлов. Он читает файл последовательно и не держит лишнего в памяти.
Параметры конструктора по порядку:
- Имя файла или поток.
- Кодировка — значение
КодировкаТекстаили строка, например"windows-1251". - Разделитель строк.
- Конвертируемый разделитель строк.
Монопольно— открывать ли файл в монопольном режиме.
Чтение всего файла в одну строку:
&НаСервереБезКонтекста
Функция ПрочитатьФайлЦеликом(ИмяФайла)
Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8);
Текст = Чтение.Прочитать();
Чтение.Закрыть();
Возврат Текст;
КонецФункции
Метод Прочитать() без параметра возвращает весь оставшийся текст. С параметром — указанное число символов: Чтение.Прочитать(1000) вернёт первые 1000 символов. Это удобно, когда нужно заглянуть в начало файла и понять, тот ли формат пришёл.
Всегда вызывайте Закрыть(). Пока объект не закрыт, файл остаётся занятым: его нельзя удалить или перезаписать, а повторный запуск обработки может упасть с ошибкой доступа.
Если объект создан без имени файла, его открывают отдельно: Чтение.Открыть(ИмяФайла, "windows-1251"). Так удобно читать несколько файлов одним объектом по очереди.
Файл занят другой программой
Типичный случай — лог или выгрузка, которую прямо сейчас пишет внешняя программа. По умолчанию ЧтениеТекста открывает файл монопольно и падает, если файл занят. Передайте пятым параметром Ложь:
Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8, , , Ложь);
Так платформа откроет файл без монопольной блокировки. Учтите: если запись ещё идёт, последняя строка может прийти обрезанной.
Построчное чтение больших файлов
Файл на сотни мегабайт целиком в строку читать не стоит: весь текст ляжет в память рабочего процесса. Читайте по одной строке и сразу обрабатывайте.
ПрочитатьСтроку() возвращает очередную строку без символа перевода строки. Когда файл кончился, метод возвращает Неопределено — это и есть условие выхода из цикла.
&НаСервереБезКонтекста
Процедура ЗагрузитьПрайс(ИмяФайла)
Чтение = Новый ЧтениеТекста(ИмяФайла, "windows-1251");
НомерСтроки = 0;
Строка = Чтение.ПрочитатьСтроку();
Пока Строка <> Неопределено Цикл
НомерСтроки = НомерСтроки + 1;
Если НомерСтроки > 1 И ЗначениеЗаполнено(СокрЛП(Строка)) Тогда
Поля = СтрРазделить(Строка, ";", Истина);
Если Поля.Количество() >= 3 Тогда
Артикул = СокрЛП(Поля[0]);
Наименование = СокрЛП(Поля[1]);
Цена = Число(СтрЗаменить(Поля[2], ",", "."));
// запись в регистр или таблицу значений
Иначе
Сообщить(СтрШаблон("Строка %1: мало полей", Формат(НомерСтроки, "ЧГ=0")));
КонецЕсли;
КонецЕсли;
Строка = Чтение.ПрочитатьСтроку();
КонецЦикла;
Чтение.Закрыть();
КонецПроцедуры
Что здесь сделано по-взрослому:
- 📌 Первая строка (заголовки колонок) пропускается по счётчику.
- 📌 Пустые строки в конце файла не вызывают ошибок.
- 📌 Номер строки выводится через
Формат(..., "ЧГ=0"), иначе на номере 1 500 появится пробел-разделитель групп. - 📌 Запятая в цене меняется на точку перед
Число().
Если Число() получит строку вроде "1 250,00" с пробелом внутри, будет исключение. Перед преобразованием уберите пробелы: СтрЗаменить(Поля[2], " ", ""). А неразрывный пробел убирают отдельно: СтрЗаменить(Значение, Символы.НПП, "").
Как ускорить загрузку тысяч строк
Само чтение файла быстрое. Тормозит то, что делается с каждой строкой:
- Не ищите элемент справочника запросом на каждой строке. Сначала соберите все артикулы в массив, одним запросом получите соответствия, затем ищите в
Соответствие. - Записывайте набор записей регистра один раз, а не по строке.
- Для очень больших файлов делите загрузку на порции и запускайте её фоновым заданием, чтобы не держать сеанс пользователя.
Кодировки текстового файла: UTF-8, Windows-1251, OEM
Кракозябры вместо кириллицы почти всегда означают, что кодировка в конструкторе не совпала с реальной кодировкой файла.
| Откуда файл | Обычная кодировка | Что указать |
|---|---|---|
| Современные сервисы, API, выгрузки из Linux | UTF-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, где в значениях встречаются точки с запятой в кавычках?
СтрРазделить такие поля разрежет неправильно. Нужен разбор с учётом кавычек: идите по строке посимвольно и переключайте флаг «внутри кавычек».