В процессе разработки на платформе 1С 7.7 программисты часто сталкиваются с задачей обработки больших массивов текстовых данных. Типичный сценарий — объединение тысяч мелких файлов (логов, протоколов расчетов, временных дампов) в один финальный документ. Стандартный объект Текст, встроенный в платформу, при попытке выполнить такую операцию «в лоб» через циклы ДобавитьСтроку() начинает работать крайне медленно. Это происходит из-за особенностей управления памятью в 1С 7.7: чем больше становится итоговый объект, тем дольше занимает каждая следующая вставка, так как системе приходится постоянно перераспределять адресное пространство.
В этой статье мы подробно разберем, почему штатные средства не справляются с большими объемами, и рассмотрим альтернативные, высокопроизводительные методы решения задачи с использованием системных утилит Windows, скриптов PowerShell и специализированных объектов ActiveX.
Рассмотрим классический код, который обычно использует начинающий разработчик для склейки файлов. Проанализируем ситуацию на примере кода из обсуждения:
ТекущийКаталог = ФС.ТекКаталог();
ФС.УстТекКаталог(ВыбКаталог);
Тлог = СоздатьОбъект("Текст");
сч = 0;
ИмяФайла = ФС.НайтиПервыйФайл("~*.tmp");
Пока ПустаяСтрока(ИмяФайла) = 0 Цикл
сч = сч + 1;
Тчасть = СоздатьОбъект("Текст");
Тчасть.Открыть(ВыбКаталог+ИмяФайла);
КС = Тчасть.КоличествоСтрок();
Для ы = 1 По КС Цикл
Тлог.ДобавитьСтроку(Тчасть.ПолучитьСтроку(ы));
КонецЦикла;
ИмяФайла = ФС.НайтиСледующийФайл();
КонецЦикла;
Тлог.Записать(ВыбКаталог + "All.txt");
Основные причины тормозов здесь следующие:
Для вызывает метод ПолучитьСтроку и ДобавитьСтроку. При наличии миллионов строк суммарные накладные расходы на вызов методов объекта становятся критическими.Текст хранит все данные в оперативной памяти. При достижении объема в десятки мегабайт 1С начинает испытывать трудности с выделением непрерывных блоков памяти, что может привести к зависанию или аварийному завершению программы.Состояние() внутри цикла также замедляет процесс, так как отрисовка статус-бара требует ресурсов процессора.Самый эффективный способ склеить файлы — делегировать эту задачу операционной системе. Команда COPY работает на уровне файловой системы и потоков ввода-вывода, игнорируя логику интерпретатора 1С.
Для простой склейки всех файлов в каталоге можно использовать следующую команду:
КомандаСистемы("copy /b " + ПутьККаталогу + "*.tmp " + ПутьККаталогу + "result.txt");
Важные нюансы использования CMD:
/b (binary) критически важен. Он заставляет систему копировать файлы как поток байтов. Если использовать ключ /a (ascii), копирование может прерваться при встрече символа конца файла (Ctrl+Z), что часто случается в логах.*.txt).* файлы склеиваются в том порядке, в котором их выдает файловая система (обычно по алфавиту). Если важен строгий хронологический порядок, лучше сформировать .bat файл программно.Рассмотрим пример генерации бат-файла для строгого контроля последовательности:
ТекстБата = СоздатьОбъект("Текст");
ИмяФайла = ФС.НайтиПервыйФайл(ВыбКаталог + "*.tmp");
ТекстБата.ДобавитьСтроку("del Combined.txt"); // Удаляем старый результат
Пока ПустаяСтрока(ИмяФайла) = 0 Цикл
// Используем >> для добавления в конец файла
ТекстБата.ДобавитьСтроку("type " + ИмяФайла + " >> Combined.txt");
ИмяФайла = ФС.НайтиСледующийФайл();
КонецЦикла;
ТекстБата.Записать(ВыбКаталог + "merge.bat");
КомандаСистемы(ВыбКаталог + "merge.bat");
Если требуется предварительная сортировка (например, по дате создания) или фильтрация содержимого, отличным инструментом станет PowerShell. Он позволяет выполнять сложные операции в одну строку.
Проанализируем пример команды для склейки с сортировкой по дате:
КомандаСистемы("powershell -Command ""Get-ChildItem -Path 'C:\Logs\' -Filter *.log | Sort-Object CreationTime | Get-Content | Out-File 'C:\Logs\Total.txt'""");
Однако помните, что стандартный Get-Content в PowerShell работает довольно медленно на огромных файлах, так как оборачивает каждую строку в объект. Для файлов объемом в сотни мегабайт лучше использовать методы .NET напрямую:
$files = Get-ChildItem "C:\Logs\*.log" | Sort-Object LastWriteTime;
[System.IO.File]::WriteAllLines("C:\Logs\Result.txt", (foreach ($f in $files) { [System.IO.File]::ReadAllLines($f.FullName) }));
Если вы не хотите зависеть от внешних командных интерпретаторов и вам нужно контролировать процесс внутри 1С, используйте объект Scripting.FileSystemObject. Выясним, как сделать это правильно, избегая метода ReadAll.
Вызов ReadAll() для файла в 50 МБ мгновенно «съест» 50 МБ оперативной памяти. Правильнее читать файлы блоками. Рассмотрим пример реализации:
ФСО = СоздатьОбъект("Scripting.FileSystemObject");
ВыходнойФайл = ФСО.CreateTextFile("C:\All.txt", -1); // -1 для создания в Unicode, 0 для ANSI
// Допустим, у нас есть список имен файлов в СпискеЗначений
Для Ном = 1 По СписокФайлов.РазмерСписка() Цикл
ПутьКФайлу = СписокФайлов.ПолучитьЗначение(Ном);
Если ФСО.FileExists(ПутьКФайлу) = 1 Тогда
ФайлИст = ФСО.OpenTextFile(ПутьКФайлу, 1); // 1 = ForReading
Пока ФайлИст.AtEndOfStream = 0 Цикл
// Читаем блоками по 128 КБ для скорости
ВыходнойФайл.Write(ФайлИст.Read(131072));
КонецЦикла;
ФайлИст.Close();
КонецЕсли;
КонецЦикла;
ВыходнойФайл.Close();
Преимущество этого метода: Минимальное потребление памяти вне зависимости от общего размера результирующего файла, так как данные проходят через небольшой буфер.
Объект ADODB.Stream является наиболее мощным средством для работы с потоками. Он позволяет легко менять кодировки (например, склеить файлы Windows-1251 и сохранить их в UTF-8) и работать с бинарными данными без искажений.
Рассмотрим алгоритм работы со стримом:
CopyTo.Метод CopyTo работает на уровне системных вызовов копирования памяти, что обеспечивает максимальную производительность, сопоставимую с командой copy /b.
Если задача склейки файлов возникает регулярно из-за того, что ваша система генерирует слишком много логов, стоит рассмотреть архитектурные изменения:
V7TextFile), которые оптимизированы для быстрой записи больших текстовых массивов и имеют встроенные механизмы буферизации.1sqlite.FSO.OpenTextFile с параметром 8 (ForAppending). Это избавит вас от необходимости последующей склейки.Подводя итог, можно сказать: если вам нужно максимально быстро и просто соединить файлы «здесь и сейчас», используйте КомандаСистемы с вызовом copy /b. Если же требуется сложная логика обработки в процессе склейки — выбирайте FileSystemObject с блочным чтением.