В процессе интеграции 1С с внешними системами часто возникает задача: прочитать текстовый файл, найти в нем определенные данные (например, реквизиты объекта), загрузить их в информационную базу и сразу же удалить обработанную строку, чтобы не возвращаться к ней повторно. На первый взгляд задача кажется простой, но она скрывает несколько технических «подводных камней», связанных с индексацией строк и записью данных на диск. В этой статье мы подробно разберем, как правильно реализовать этот механизм.
Разберем типичную ситуацию. Когда мы используем объект ТекстовыйДокумент, строки в нем пронумерованы от 1 до N. Если мы запустим цикл от 1 до N и удалим, скажем, вторую строку, то третья строка мгновенно станет второй, четвертая — третьей и так далее. В результате наш цикл «перепрыгнет» через одну строку, а в конце выдаст ошибку, так как общее количество строк уменьшилось.
Рассмотрим, как правильно организовать этот процесс, используя наиболее эффективные подходы.
Это самый простой способ, который подходит для файлов небольшого и среднего размера. Основная хитрость здесь заключается в том, чтобы обходить строки с конца в начало. При удалении последней или предпоследней строки индексы тех, что находятся выше, не меняются, и работа цикла не нарушается.
Проанализируем программный код для этого метода:
// Создаем объект для работы с текстом
МассивФайлов = НайтиФайлы("C:\Exchange\", "*.txt");
Текст = Новый ТекстовыйДокумент;
Для Каждого Файл Из МассивФайлов Цикл
Текст.Прочитать(Файл.ПолноеИмя);
// Получаем количество строк перед началом обхода
ВсегоСтрок = Текст.КоличествоСтрок();
// Обходим строки С КОНЦА к началу
Для НомерСтроки = -ВсегоСтрок По -1 Цикл
ТекущийНомер = -НомерСтроки;
Стр = Текст.ПолучитьСтроку(ТекущийНомер);
МассивРеквизитов = СтрРазделить(Стр, "-", Ложь);
// Проверяем, что строка содержит нужные данные (защита от пустых строк)
Если МассивРеквизитов.Количество() >= 4 Тогда
Если Объект.Реквизит1 = МассивРеквизитов[0] Тогда
// Заполняем данные в 1С
Объект.Реквизит2 = МассивРеквизитов[1];
Объект.Реквизит3 = МассивРеквизитов[2];
Объект.Реквизит4 = МассивРеквизитов[3];
// Удаляем строку из объекта в памяти
Текст.УдалитьСтроку(ТекущийНомер);
Сообщить("Строка удалена: " + Стр);
КонецЕсли;
КонецЕсли;
КонецЦикла;
// КРИТИЧЕСКИ ВАЖНО: записываем изменения обратно в файл
Текст.Записать(Файл.ПолноеИмя);
КонецЦикла;
Важный нюанс: метод УдалитьСтроку() принимает в качестве параметра номер строки (Число), а не саму строку (Текст). Попытка передать туда переменную со строковым значением приведет к ошибке «Несоответствие типов».
Если логика обработки сложная и вы не хотите удалять строки прямо внутри цикла чтения, можно сначала собрать номера строк, подлежащих удалению, в отдельный массив. Однако и здесь важно соблюдать порядок удаления.
Разберем по шагам:
Массив для хранения номеров строк.УдаляемыеСтроки.Добавить(НомерСтроки);.УдалитьСтроку().Посмотрим на пример реализации:
УдаляемыеСтроки = Новый Массив;
Для НомерСтроки = 1 По Текст.КоличествоСтрок() Цикл
Стр = Текст.ПолучитьСтроку(НомерСтроки);
// ... логика проверки условия ...
Если УсловиеВыполнено Тогда
УдаляемыеСтроки.Добавить(НомерСтроки);
КонецЕсли;
КонецЦикла;
// Удаляем с конца, чтобы не «поплыли» индексы
Для Счетчик = 1 По УдаляемыеСтроки.Количество() Цикл
ИндексМассива = УдаляемыеСтроки.Количество() - Счетчик;
НомерДляУдаления = УдаляемыеСтроки[ИндексМассива];
Текст.УдалитьСтроку(НомерДляУдаления);
КонецЦикла;
Текст.Записать(ПутьКФайлу);
Если в файле много строк или структура данных нестабильна, самый надежный способ — не удалять строки из ТекстовыйДокумент, а сформировать новый текст только из тех строк, которые не нужно удалять.
Проанализируем этот алгоритм:
НовыйТекст = Новый ТекстовыйДокумент;
Для НомерСтроки = 1 По Текст.КоличествоСтрок() Цикл
Стр = Текст.ПолучитьСтроку(НомерСтроки);
// Если строка НЕ подходит под условие удаления, добавляем её в новый объект
Если НЕ СтрокаДолжнаБытьУдалена(Стр) Тогда
НовыйТекст.ДобавитьСтроку(Стр);
КонецЕсли;
КонецЦикла;
НовыйТекст.Записать(ПутьКФайлу);
Этот метод исключает любые ошибки с индексами и считается более «чистым» с точки зрения программирования.
Выясним причины наиболее частых сбоев при работе с текстовыми файлами:
1. Ошибка «Индекс находится за границами массива»
Эта ошибка часто возникает при использовании функции СтрРазделить. Если в конце файла есть пустая строка или строка с некорректным количеством разделителей, обращение к МассивРеквизитов[1] вызовет падение программы. Всегда проверяйте количество элементов перед обращением к ним: Если МассивРеквизитов.Количество() > 1 Тогда....
2. Изменения не сохраняются в файле
Методы УдалитьСтроку() или ДобавитьСтроку() работают только с объектом 1С, находящимся в оперативной памяти. Чтобы изменения отразились на жестком диске, необходимо обязательно вызвать метод Записать(ПолноеИмяФайла). Без этого после закрытия обработки файл останется прежним.
3. Проблемы с кодировкой
По умолчанию 1С может сохранять файлы в кодировке UTF-8 или ANSI в зависимости от настроек системы. Если после записи вы видите непонятные символы («кракозябры»), явно указывайте кодировку в методах Прочитать и Записать. Например: Текст.Прочитать(Путь, КодировкаТекста.UTF8);.
Представьте ситуацию: вы начали записывать измененный файл, и в этот момент произошел сбой питания. Файл может оказаться «обрубленным» или пустым. Для критически важных данных рекомендуем использовать следующую схему:
ПолучитьИмяВременногоФайла()).УдалитьФайлы().ПереместитьФайл().Такой подход гарантирует, что у вас всегда останется либо старая версия данных, либо полностью обновленная новая версия, без промежуточных поврежденных состояний.
Мы рассмотрели несколько вариантов решения задачи. Для простых скриптов очистки логов или небольших файлов обмена идеально подходит обратный цикл (от N до 1) с использованием объекта ТекстовыйДокумент. Для высоконагруженных систем лучше использовать потоковую перезапись через временные файлы. Главное — помнить о проверке границ массивов и обязательной фиксации изменений методом Записать().