Перебор и чтение файлов каталога на встроенном языке 1С 8.3
Содержание
Чтобы прочитать все файлы в папке в 1С, получите их список функцией НайтиФайлы(Каталог, Маска), а затем в цикле откройте каждый файл через ЧтениеТекста или ТекстовыйДокумент. Для подпапок у НайтиФайлы есть третий параметр — Истина включает поиск во всех вложенных каталогах.
Фрагменты кода рассчитаны на платформу 8.3: синхронный и асинхронный перебор, фильтр по расширению и дате, чтение в нужной кодировке, обработка ошибок доступа и ручная рекурсия с ограничением глубины. Код одинаково работает в любой конфигурации, потому что это механизмы платформы, а не БП или УТ.
Какие объекты платформы 1С работают с файлами и папками
Весь набор умещается в несколько функций и типов. Их хватает на типовые задачи загрузки из каталога: выписки, прайсы, CSV из сторонних систем, XML от контрагентов.
| Что нужно | Чем делать | Комментарий |
|---|---|---|
| Получить список файлов | НайтиФайлы() / НайтиФайлыАсинх() | возвращает массив объектов Файл |
| Узнать свойства файла | объект Файл | имя, расширение, путь, размер, дата изменения |
| Прочитать текст целиком | ТекстовыйДокумент.Прочитать() | удобно для небольших файлов |
| Прочитать текст построчно | ЧтениеТекста | экономит память на больших файлах |
| Прочитать файл как байты | Новый ДвоичныеДанные(ИмяФайла) | для любых форматов, хеша, отправки |
| Разобрать XML / JSON | ЧтениеXML, ЧтениеJSON | структурированные данные |
| Открыть Excel | ТабличныйДокумент.Прочитать() | xlsx, xls, ods |
| Переместить, скопировать, удалить | ПереместитьФайл(), КопироватьФайл(), УдалитьФайлы() | архивирование после загрузки |
Две вспомогательные функции избавляют от проблем с переносимостью между Windows и Linux:
- 🔹
ПолучитьРазделительПути()— вернёт\на Windows и/на Linux; - 🔹
ПолучитьМаскуВсеФайлы()— вернёт маску «все файлы», правильную для текущей ОС (*.*или*).
У объекта Файл чаще всего нужны:
- 📄
Имя— имя с расширением:выписка_01.txt; - 📄
ИмяБезРасширения—выписка_01; - 📄
Расширение— с точкой:.txt; - 📄
ПолноеИмя— полный путь, его и передают в методы чтения; - 📄
Путь— каталог, где лежит файл; - ⚙️ методы
ЭтоФайл(),ЭтоКаталог(),Существует(),Размер(),ПолучитьВремяИзменения(),ПолучитьНевидимость().
Свойство Расширение содержит точку. Сравнение Файл.Расширение = "txt" всегда даст Ложь — сравнивайте с ".txt".
Как получить список всех файлов в каталоге 1С
Базовый перебор выглядит так. Код выполняется на сервере или в толстом клиенте — там, где синхронные вызовы разрешены.
Функция СписокФайловПапки(Каталог)
Результат = Новый Массив;
Найденные = НайтиФайлы(Каталог, ПолучитьМаскуВсеФайлы(), Ложь);
Для Каждого Элемент Из Найденные Цикл
Если Элемент.ЭтоФайл() Тогда
Результат.Добавить(Элемент.ПолноеИмя);
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Порядок действий:
- Передайте путь к каталогу без завершающего разделителя:
C:\Обмен\Входящиеили\\srv-files\обмен\входящие. - Укажите маску. Для всех файлов —
ПолучитьМаскуВсеФайлы(). - Третий параметр
Ложь— только верхний уровень, без подпапок. - Отсейте каталоги проверкой
ЭтоФайл():НайтиФайлывозвращает и файлы, и папки, подходящие под маску.
Асинхронный вариант для тонкого клиента
В управляемых формах с запретом синхронных вызовов (свойство конфигурации «Режим использования синхронных вызовов расширений платформы и внешних компонент» = «Не использовать») код выше на клиенте упадёт. Начиная с платформы 8.3.18 используйте Асинх / Ждать:
&НаКлиенте
Асинх Процедура ПрочитатьПапку(Команда)
Найденные = Ждать НайтиФайлыАсинх(КаталогЗагрузки, "*.csv", Ложь);
Для Каждого Элемент Из Найденные Цикл
ЭтоФайл = Ждать Элемент.ЭтоФайлАсинх();
Если ЭтоФайл Тогда
Сообщить(Элемент.ПолноеИмя);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
На платформах ниже 8.3.18 аналог — НачатьПоискФайлов() с ОписаниеОповещения.
Как дать пользователю выбрать папку
Чтобы не вписывать путь в код, покажите диалог выбора каталога:
&НаКлиенте
Асинх Процедура ВыбратьКаталог(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
Диалог.Заголовок = "Папка с файлами для загрузки";
Выбрано = Ждать Диалог.ВыбратьАсинх();
Если Выбрано <> Неопределено Тогда
КаталогЗагрузки = Выбрано[0];
КонецЕсли;
КонецПроцедуры
Чтение содержимого текстовых файлов из папки
Способ зависит от размера файла и от того, что вы с ним делаете дальше.
| Способ | Когда подходит | Минус |
|---|---|---|
ТекстовыйДокумент | файлы до нескольких мегабайт, нужен доступ к строке по номеру | весь файл в памяти |
ЧтениеТекста | большие логи, выгрузки на сотни тысяч строк | только последовательное чтение |
ДвоичныеДанные + ПолучитьСтрокуИзДвоичныхДанных() | файл уже пришёл с клиента как двоичные данные | весь файл в памяти |
Целиком через ТекстовыйДокумент
Текст = Новый ТекстовыйДокумент;
Текст.Прочитать(ПолноеИмя, КодировкаТекста.UTF8);
Для НомерСтроки = 1 По Текст.КоличествоСтрок() Цикл
Строка = Текст.ПолучитьСтроку(НомерСтроки);
// разбор строки
КонецЦикла;
Нумерация строк в ТекстовыйДокумент начинается с 1, а не с 0.
Построчно через ЧтениеТекста
Чтение = Новый ЧтениеТекста(ПолноеИмя, КодировкаТекста.UTF8);
Строка = Чтение.ПрочитатьСтроку();
Пока Строка <> Неопределено Цикл
Части = СтрРазделить(Строка, ";", Истина);
// Части[0] — первая колонка, Части[1] — вторая
Строка = Чтение.ПрочитатьСтроку();
КонецЦикла;
Чтение.Закрыть();
ПрочитатьСтроку() возвращает Неопределено, когда файл закончился. Пустая строка в середине файла вернётся как "", поэтому цикл на ней не прервётся.
Всегда вызывайте Закрыть(): пока объект чтения жив, файл остаётся занят, и его не получится переместить в архив или удалить.
Кодировка: самая частая причина «кракозябр»
- 🔤 Выгрузки из современных систем и большинство XML —
КодировкаТекста.UTF8. - 🔤 Старые выгрузки из Windows-программ и клиент-банков —
КодировкаТекста.ANSIили строка"windows-1251". - 🔤 Файлы из DOS-программ —
КодировкаТекста.OEM(cp866).
Если источник присылает файлы в разных кодировках, проверьте первую строку: при чтении не в той кодировке вместо кириллицы появятся символы вроде Выписка. По такому признаку можно перечитать файл во второй кодировке.
Не только текст: XML, JSON и Excel
Для структурированных файлов не разбирайте строки вручную:
- 🧩 XML —
ЧтениеXMLиОткрытьФайл(ПолноеИмя), затемФабрикаXDTO.ПрочитатьXML()или ручной проход по узлам; - 🧩 JSON —
ЧтениеJSON.ОткрытьФайл(ПолноеИмя)иПрочитатьJSON(Чтение); - 🧩 Excel —
ТабличныйДокумент.Прочитать(ПолноеИмя), после чего ячейки доступны черезПолучитьОбласть()иОбласть("R2C1").Текст.
Фильтр файлов в папке по расширению, имени и дате
Маска в НайтиФайлы понимает * (любые символы) и ? (один символ). Примеры:
"*.csv"— все CSV;"выписка_*.txt"— файлы, чьё имя начинается с «выписка_»;"2026??.xml"— шесть символов имени, первые четыре фиксированы.
Ограничение: маска одна на вызов. Если нужны сразу .xml и .txt, ищите все файлы и фильтруйте в коде:
Допустимые = Новый Соответствие;
Допустимые.Вставить(".xml", Истина);
Допустимые.Вставить(".txt", Истина);
Для Каждого Элемент Из НайтиФайлы(Каталог, ПолучитьМаскуВсеФайлы()) Цикл
Если Не Элемент.ЭтоФайл() Тогда
Продолжить;
КонецЕсли;
Если Допустимые[НРег(Элемент.Расширение)] = Неопределено Тогда
Продолжить;
КонецЕсли;
Если Элемент.ПолучитьНевидимость() Тогда
Продолжить; // скрытые и служебные файлы
КонецЕсли;
Если Элемент.ПолучитьВремяИзменения() < НачалоДня(ТекущаяДата()) Тогда
Продолжить; // только сегодняшние
КонецЕсли;
// чтение файла
КонецЦикла;
На Linux-сервере имена файлов чувствительны к регистру: маска *.xml не найдёт ORDER.XML. Поэтому надёжнее искать все файлы и приводить расширение к нижнему регистру через НРег(), как в примере.
Ещё два фильтра, которые часто нужны на практике:
- 🚫 Пропуск временных файлов Office — их имя начинается с
~$:СтрНачинаетсяС(Элемент.Имя, "~$"). - 🚫 Пропуск пустых файлов —
Элемент.Размер() = 0. Такие появляются, когда внешняя программа ещё не дописала выгрузку.
Где выполняется код и почему 1С «не видит» папку
Если файлы в папке есть, а НайтиФайлы вернул пустой массив, первым делом проверьте, на каком компьютере выполняется код.
Отсюда практическое правило:
- Файлы на компьютере пользователя — ищите и читайте на клиенте, затем передавайте содержимое на сервер.
- Файлы в общей папке, которую видит сервер 1С, — работайте целиком на сервере. Это быстрее: нет передачи данных между клиентом и сервером.
- Для сетевой папки выдайте права на чтение (а при архивировании — и на изменение) учётной записи службы 1С. Её имя видно в свойствах службы агента сервера в оснастке «Службы» Windows.
Обработка ошибок при чтении файлов из каталога
Файл может быть открыт другой программой, удалён между поиском и чтением, лежать в папке без прав. Одна такая ошибка без обработки прервёт загрузку всех остальных файлов. Оборачивайте чтение каждого файла в отдельный блок Попытка:
Чтение = Неопределено;
Для Каждого ИмяФайла Из СписокФайлов Цикл
Попытка
Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8);
ОбработатьФайл(Чтение);
Чтение.Закрыть();
Исключение
Если Чтение <> Неопределено Тогда
Чтение.Закрыть();
КонецЕсли;
ЗаписьЖурналаРегистрации("Загрузка из каталога",
УровеньЖурналаРегистрации.Ошибка, , ИмяФайла,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Чтение = Неопределено;
КонецЦикла;
Что здесь сделано:
- ✅ ошибка одного файла не останавливает цикл;
- ✅ открытый файл закрывается и в ветке
Исключение; - ✅ причина пишется в журнал регистрации с именем файла — администратор найдёт её через отбор по событию «Загрузка из каталога»;
- ✅ переменная
Чтениеобнуляется, чтобы проверка в следующей итерации не сработала на старом объекте.
Перед вызовом поиска проверьте сам каталог — так пользователь получит понятное сообщение вместо пустого результата:
Папка = Новый Файл(Каталог);
Если Не Папка.Существует() Или Не Папка.ЭтоКаталог() Тогда
ВызватьИсключение СтрШаблон("Каталог %1 не найден или недоступен", Каталог);
КонецЕсли;
Если внешняя система пишет файлы в ту же папку, из которой вы читаете, договоритесь о схеме «записал во временное имя .tmp — переименовал». Читайте только файлы с итоговым расширением, и полузаписанный файл в загрузку не попадёт.
Рекурсивный обход вложенных папок
Самый короткий путь — третий параметр НайтиФайлы:
ВсеФайлы = НайтиФайлы(Каталог, "*.xml", Истина);
Платформа сама пройдёт по всем уровням вложенности. Минусы: нельзя ограничить глубину, нельзя пропустить отдельные папки (например, Архив), а на дереве с десятками тысяч файлов весь массив собирается в памяти до первого чтения.
Когда нужен контроль, пишите рекурсию сами:
Процедура ОбойтиКаталог(Каталог, Результат, Глубина = 0)
МаксимальнаяГлубина = 5;
Если Глубина > МаксимальнаяГлубина Тогда
Возврат;
КонецЕсли;
Для Каждого Элемент Из НайтиФайлы(Каталог, ПолучитьМаскуВсеФайлы(), Ложь) Цикл
Если Элемент.ЭтоКаталог() Тогда
Если НРег(Элемент.Имя) = "архив" Тогда
Продолжить;
КонецЕсли;
ОбойтиКаталог(Элемент.ПолноеИмя, Результат, Глубина + 1);
ИначеЕсли НРег(Элемент.Расширение) = ".xml" Тогда
Результат.Добавить(Элемент.ПолноеИмя);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Вызов: Файлы = Новый Массив; ОбойтиКаталог("\\srv-files\обмен", Файлы);.
Глубина 5 здесь — страховка от бесконечного обхода по символическим ссылкам и точкам соединения NTFS, которые указывают на родительскую папку.
Полный пример: загрузить все файлы папки и убрать их в архив
Сценарий из практики: банк или склад кладёт CSV в общую папку, регламентное задание раз в 15 минут забирает их, загружает строки и перемещает обработанные файлы в подпапку Архив, а сбойные — в Ошибки.
Процедура ЗагрузитьФайлыИзКаталога(Каталог) Экспорт
Разделитель = ПолучитьРазделительПути();
КаталогАрхива = Каталог + Разделитель + "Архив";
КаталогОшибок = Каталог + Разделитель + "Ошибки";
СоздатьКаталог(КаталогАрхива);
СоздатьКаталог(КаталогОшибок);
Для Каждого Элемент Из НайтиФайлы(Каталог, ПолучитьМаскуВсеФайлы(), Ложь) Цикл
Если Не Элемент.ЭтоФайл() Или НРег(Элемент.Расширение) <> ".csv" Тогда
Продолжить;
КонецЕсли;
Чтение = Неопределено;
Попытка
Чтение = Новый ЧтениеТекста(Элемент.ПолноеИмя, КодировкаТекста.UTF8);
ЗагруженоСтрок = 0;
Строка = Чтение.ПрочитатьСтроку(); // строка заголовков
Строка = Чтение.ПрочитатьСтроку();
Пока Строка <> Неопределено Цикл
Если ЗначениеЗаполнено(Строка) Тогда
ЗагрузитьСтроку(СтрРазделить(Строка, ";", Истина));
ЗагруженоСтрок = ЗагруженоСтрок + 1;
КонецЕсли;
Строка = Чтение.ПрочитатьСтроку();
КонецЦикла;
Чтение.Закрыть();
Чтение = Неопределено;
ПереместитьФайл(Элемент.ПолноеИмя,
КаталогАрхива + Разделитель + Элемент.Имя);
ЗаписьЖурналаРегистрации("Загрузка из каталога",
УровеньЖурналаРегистрации.Информация, , Элемент.Имя,
СтрШаблон("Загружено строк: %1", Формат(ЗагруженоСтрок, "ЧГ=0")));
Исключение
ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
Если Чтение <> Неопределено Тогда
Чтение.Закрыть();
КонецЕсли;
ЗаписьЖурналаРегистрации("Загрузка из каталога",
УровеньЖурналаРегистрации.Ошибка, , Элемент.Имя, ТекстОшибки);
Попытка
ПереместитьФайл(Элемент.ПолноеИмя,
КаталогОшибок + Разделитель + Элемент.Имя);
Исключение
// файл занят другой программой — попробуем в следующий запуск
КонецПопытки;
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Что важно в этом примере:
СоздатьКаталог()не падает, если папка уже есть, — вызывать его при каждом запуске безопасно.- Поиск идёт с параметром
Ложь. Иначе файлы изАрхивпри следующем запуске попали бы в загрузку повторно. - Файл перемещается только после успешного чтения всех строк. Если загрузка упала на середине, файл уйдёт в
Ошибки, а не в архив. - Счётчик строк выводится через
Формат(..., "ЧГ=0"), чтобы в журнале не было пробелов-разделителей разрядов. ЗагрузитьСтроку()— ваша процедура записи данных в базу.
Если в архиве уже лежит файл с тем же именем, ПереместитьФайл() вызовет исключение. Для ежедневных выгрузок с повторяющимися именами добавляйте к имени в архиве дату и время: Формат(ТекущаяДатаСеанса(), "ДФ=yyyyMMdd_HHmmss").
Чтобы такая загрузка шла по расписанию, процедуру вызывают из обработчика регламентного задания в общем модуле с флажком «Сервер». Права на папку в этом случае нужны учётной записи службы сервера 1С.
Частые вопросы
Можно ли получить количество файлов в папке без перебора?
НайтиФайлы(Каталог, "*.csv").Количество(). Но платформа всё равно соберёт массив объектов, поэтому на десятках тысяч файлов это займёт заметное время.В каком порядке НайтиФайлы возвращает файлы?
СписокЗначений и вызовите СортироватьПоЗначению(), или отсортируйте по дате изменения через таблицу значений.Как прочитать файл, который сейчас открыт в Excel?
КопироватьФайл() во временный каталог (КаталогВременныхФайлов()) и читать копию, затем удалить её через УдалитьФайлы().Почему в веб-клиенте НайтиФайлы недоступна?
Чем отличаются УдалитьФайлы(Каталог, "*.*") и удаление каталога целиком?
УдалитьФайлы(Каталог) без маски удаляет каталог вместе со всем содержимым — используйте его только для своих временных папок.