При разработке механизмов импорта данных из внешних текстовых файлов перед программистом 1С всегда встает важный архитектурный вопрос: как правильно разделить работу между клиентской частью и сервером? В этой статье мы подробно разберем ситуацию на примере загрузки ТВ-программы в периодический регистр сведений, проанализируем различные подходы к парсингу «замусоренных» файлов и выясним, как сделать процесс максимально быстрым и стабильным.
Рассмотрим ситуацию: у нас есть файл размером около 25 Кб, содержащий как полезную информацию (строки с ТВ-передачами), так и лишние данные («мусор» в начале и конце файла). Согласно канонам разработки на платформе 1С:Предприятие 8.3, бизнес-логика и тяжелые операции с данными должны выполняться на стороне сервера. Клиентская часть предназначена в первую очередь для взаимодействия с пользователем и выбора файла.
Выделим три основных варианта решения задачи:
Проанализируем эти варианты. Для небольших файлов (до нескольких мегабайт) наиболее правильным является первый вариант. Это позволяет инкапсулировать всю логику обработки в одном серверном модуле, что упрощает поддержку кода и тестирование. Однако способ передачи данных играет ключевую роль.
Многие начинающие разработчики пытаются передать содержимое файла в виде одной длинной строки в параметрах процедуры. Разберем, почему это не лучший путь. При передаче строки между клиентом и сервером происходит процесс сериализации в формат XML, что может увеличить объем передаваемого трафика в несколько раз. Для файла 25 Кб это не критично, но для файлов большего объема это вызовет заметные задержки.
Посмотрим на рекомендуемый метод с использованием объекта ДвоичныеДанные и временного хранилища. Этот подход позволяет передать файл в бинарном виде, минуя тяжелую текстовую сериализацию.
// Код на стороне Клиента
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
ПараметрыВыбора = Новый ПараметрыДиалогаВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ПараметрыВыбора.Фильтр = "Текстовый файл (*.txt)|*.txt";
// Помещаем файл во временное хранилище
ОписаниеФайла = Новый ОписаниеПередаваемогоФайла(ВыбранныйПуть);
МассивФайлов = Новый Массив;
МассивФайлов.Добавить(ОписаниеФайла);
ПомещенныеФайлы = ПоместитьФайлыНаСервер(,,, МассивФайлов, ЭтаФорма.УникальныйИдентификатор);
Если ПомещенныеФайлы.Количество() > 0 Тогда
ОбработатьФайлНаСервере(ПомещенныеФайлы[0].Хранение);
КонецЕсли;
КонецПроцедуры
После того как мы получили адрес файла во временном хранилище на сервере, нам нужно извлечь данные и разобрать их. Выясним, какой инструмент лучше всего подходит для чтения. Вместо загрузки всего текста в память методом Разделить(), мы воспользуемся объектом ЧтениеТекста. Это позволит нам считывать файл построчно, что значительно экономит оперативную память.
Рассмотрим подробнее логику обработки «мусора». Вместо того чтобы заранее определять границы полезных данных, мы реализуем алгоритм с проверкой соответствия строки формату времени. Проанализируем пример кода:
&НаСервере
Процедура ОбработатьФайлНаСервере(АдресХранилища)
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
ИмяФайла = ПолучитьИмяВременногоФайла("txt");
ДвоичныеДанные.Записать(ИмяФайла);
ТекстФайла = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8);
ТаблицаПередач = СоздатьТаблицуДляЗаписи(); // ТЗ для накопления данных
СтрокаТекста = ТекстФайла.ПрочитатьСтроку();
Пока СтрокаТекста <> Неопределено Цикл
// Проверяем, начинается ли строка со времени (например, "00:00")
Если ЭтоСтрокаПрограммы(СтрокаТекста) Тогда
НоваяЗапись = ТаблицаПередач.Добавить();
НоваяЗапись.Время = Лев(СтрокаТекста, 5);
НоваяЗапись.Название = СокрЛП(Сред(СтрокаТекста, 6));
КонецЕсли;
СтрокаТекста = ТекстФайла.ПрочитатьСтроку();
КонецЦикла;
ТекстФайла.Закрыть();
ЗаписатьДанныеВРегистр(ТаблицаПередач);
КонецПроцедуры
Использование функции Лев() для получения времени — это хрупкий подход. Любой лишний пробел в начале строки приведет к ошибке. Разберем более профессиональный метод с использованием регулярных выражений (RegExp). Это позволит четко разделять время и название передачи, даже если структура строки немного плавает.
Посмотрим на пример шаблона регулярного выражения: ^(\d{2}:\d{2})\s+(.*)$. Он означает: искать в начале строки две цифры, двоеточие, еще две цифры, затем один или более пробелов и забирать остаток строки как вторую группу.
// Пример функции парсинга через RegExp
Функция РазобратьСтрокуПрограммы(Знач ВходнаяСтрока)
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Ложь;
RegExp.Pattern = "^(\d{2}:\d{2})\s+(.*)$";
Matches = RegExp.Execute(ВходнаяСтрока);
Если Matches.Count > 0 Тогда
Результат = Новый Структура("Время, Название");
Результат.Время = Matches.Item(0).SubMatches.Item(0);
Результат.Название = Matches.Item(0).SubMatches.Item(1);
Возврат Результат;
КонецЕсли;
Возврат Неопределено;
КонецФункции
Одной из самых частых ошибок является запись каждой строки файла в регистр сведений по отдельности с помощью РегистрСведенийМенеджерЗаписи. Это создает отдельную транзакцию на каждую строку, что крайне медленно. Если в вашей программе 100 передач, база данных выполнит 100 операций записи.
Правильнее использовать РегистрСведенийНаборЗаписей. Мы соберем все данные в таблицу значений или массив на сервере, а затем запишем их одной транзакцией. Это ускорит процесс в десятки раз. Рассмотрим, как это реализовать:
Процедура ЗаписатьДанныеВРегистр(ТаблицаДанных)
НачатьТранзакцию();
Попытка
Набор = РегистрыСведений.ТВПрограмма.СоздатьНаборЗаписей();
// Если нужно заменить данные за конкретную дату:
// Набор.Отбор.Период.Установить(ДатаПрограммы);
Для Каждого Стр Из ТаблицаДанных Цикл
НоваяЗапись = Набор.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, Стр);
// Устанавливаем Период и другие измерения
КонецЦикла;
Набор.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации("Загрузка ТВ", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
ВызватьИсключение "Ошибка при сохранении данных в регистр!";
КонецПопытки;
КонецПроцедуры
Проанализируем еще несколько важных моментов, которые сделают ваше решение по-настоящему качественным:
НачатьТранзакцию() ... ЗафиксироватьТранзакцию(). Если в середине файла встретится некорректная строка, база данных не будет замусорена «половинчатыми» данными.ЧтениеТекста всегда указывайте кодировку явно (например, Windows-1251). Это избавит вас от проблем с «кракозябрами» вместо кириллицы.ФоновыеЗадания.Выполнить(). Это позволит пользователю продолжать работу с интерфейсом 1С, пока сервер занят обработкой файла.ЖурналРегистрации. Это поможет быстро выяснить причину, если файл вдруг изменит свой формат и парсинг перестанет работать.Подводя итог, можно сказать, что наиболее эффективный способ — это передача бинарных данных на сервер через временное хранилище с последующим использованием регулярных выражений и набора записей. Такой подход гарантирует высокую скорость работы, надежность и чистоту кода.