При интеграции 1С со сторонними системами (банковскими шлюзами, терминалами, устаревшим учетным ПО) часто приходится сталкиваться с текстовыми файлами формата Fixed-width (Flat file). В таких файлах отсутствуют привычные символы-разделители (запятая, точка с запятой, символ табуляции), а все данные идут сплошной строкой. Каждая колонка имеет строго регламентированную длину, а короткие значения дополняются пробелами или ведущими нулями.
Разберем основные и наиболее эффективные способы чтения и разбора таких данных в 1С:Предприятие.
Для файлов любого объема (включая десятки и сотни тысяч строк) наиболее быстрым и нетребовательным к оперативной памяти является использование объекта ЧтениеТекста в сочетании со встроенной строковой функцией Сред().
Рассмотрим порядок разбора по шагам:
КодировкаТекста.OEM для DOS-файлов или системной кодовой страницы CP866).ПрочитатьСтроку() до конца файла (пока значение не равно Неопределено).Сред().СокрЛП().Посмотрим на базовый пример реализации:
ПутьКФайлу = "C:\Exchange\bank_statement.txt";
ТекстФайл = Новый ЧтениеТекста(ПутьКФайлу, "CP866");
СтрокаФайла = ТекстФайл.ПрочитатьСтроку();
Пока СтрокаФайла <> Неопределено Цикл
// Проверяем, что строка не пустая
Если Не ПустаяСтрока(СтрокаФайла) Тогда
РеквизитДата = СокрЛП(Сред(СтрокаФайла, 1, 8)); // Позиция 1, длина 8
РеквизитКодБанка = СокрЛП(Сред(СтрокаФайла, 9, 6)); // Позиция 9, длина 6
РеквизитСчет = СокрЛП(Сред(СтрокаФайла, 15, 20)); // Позиция 15, длина 20
РеквизитУНП = СокрЛП(Сред(СтрокаФайла, 35, 9)); // Позиция 35, длина 9
РеквизитСумма = СокрЛП(Сред(СтрокаФайла, 44, 15)); // Позиция 44, длина 15
// Обработка и запись полученных реквизитов...
КонецЕсли;
СтрокаФайла = ТекстФайл.ПрочитатьСтроку();
КонецЦикла;
ТекстФайл.Закрыть();
Если структура файла содержит много полей или может со временем меняться, жестко прописывать вызовы функций в коде неудобно. Проанализируем более гибкий вариант: опишем структуру файла в виде таблицы или массива структур, а затем выполним разбор в универсальном цикле.
Разберем пример создания схемы и универсального чтения:
// 1. Формируем описание структуры файла
СхемаПолей = Новый ТаблицаЗначений;
СхемаПолей.Колонки.Добавить("ИмяПоля", Новый ОписаниеТипов("Строка"));
СхемаПолей.Колонки.Добавить("Позиция", Новый ОписаниеТипов("Число"));
СхемаПолей.Колонки.Добавить("Длина", Новый ОписаниеТипов("Число"));
СхемаПолей.Колонки.Добавить("Тип", Новый ОписаниеТипов("Строка"));
// Заполняем правила колонок
Стр = СхемаПолей.Добавить();
Стр.ИмяПоля = "ДатаОперации"; Стр.Позиция = 1; Стр.Длина = 8; Стр.Тип = "Дата";
Стр = СхемаПолей.Добавить();
Стр.ИмяПоля = "КодБанка"; Стр.Позиция = 9; Стр.Длина = 6; Стр.Тип = "Строка";
Стр = СхемаПолей.Добавить();
Стр.ИмяПоля = "Счет"; Стр.Позиция = 15; Стр.Длина = 20; Стр.Тип = "Строка";
Стр = СхемаПолей.Добавить();
Стр.ИмяПоля = "Сумма"; Стр.Позиция = 35; Стр.Длина = 12; Стр.Тип = "Число";
// 2. Выполняем разбор файла
ТекстФайл = Новый ЧтениеТекста(ПутьКФайлу, "CP866");
СтрокаФайла = ТекстФайл.ПрочитатьСтроку();
Пока СтрокаФайла <> Неопределено Цикл
Если Не ПустаяСтрока(СтрокаФайла) Тогда
ДанныеСтроки = Новый Структура;
Для Каждого Настройка Из СхемаПолей Цикл
ЗначениеСтр = СокрЛП(Сред(СтрокаФайла, Настройка.Позиция, Настройка.Длина));
Если Настройка.Тип = "Число" Тогда
// Заменяем разделитель при наличии и преобразуем к числу
ЗначениеСтр = СтрЗаменить(ЗначениеСтр, ",", ".");
Значение = ?(ПустаяСтрока(ЗначениеСтр), 0, Число(ЗначениеСтр));
ИначеЕсли Настройка.Тип = "Дата" Тогда
// Преобразование формата ДДММГГГГ
День = Число(Сред(ЗначениеСтр, 1, 2));
Месяц = Число(Сред(ЗначениеСтр, 3, 2));
Год = Число(Сред(ЗначениеСтр, 5, 4));
Значение = Дата(Год, Месяц, День);
Иначе
Значение = ЗначениеСтр;
КонецЕсли;
ДанныеСтроки.Вставить(Настройка.ИмяПоля, Значение);
КонецЦикла;
// ДанныеСтроки содержит готовую структуру с типизированными значениями
КонецЕсли;
СтрокаФайла = ТекстФайл.ПрочитатьСтроку();
КонецЦикла;
ТекстФайл.Закрыть();
При разборе фиксированных форматов важно учитывать следующие технические нюансы:
CP866 или Windows-1251). При открытии таких файлов в многобайтовой кодировке (например, UTF-8) русские буквы занимают по 2 байта, что приведет к сдвигу всех последующих колонок при посимвольном чтении. Всегда указывайте точную кодовую страницу источника.0000015000 соответствует сумме 150.00). Для корректного получения значения строку необходимо преобразовать через функцию Число() и разделить на 100.ГГГГММДД или ДДММГГГГ. Для их преобразования выделяйте год, месяц и день отдельными вызовами Сред() и передавайте в конструктор Дата(Год, Месяц, День).Если формат строки допускает вариативность или разбор выполняется на платформе Windows, можно применить COM-объект VBScript.RegExp.
Посмотрим на пример парсинга строки с группами захвата:
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.Multiline = Ложь;
RegExp.IgnoreCase = Истина;
RegExp.Global = Ложь;
// Описываем шаблон строки: 8 цифр даты, 6 цифр банка, 20 знаков счета, остаток суммы
RegExp.Pattern = "^(\d{8})(\d{6})(\w{20})([\d\.\,]+)$";
Если RegExp.Test(СтрокаРазбор) Тогда
Совпадения = RegExp.Execute(СтрокаРазбор);
Группы = Совпадения.Item(0).SubMatches;
РеквизитДата = Группы.Item(0);
РеквизитБанк = Группы.Item(1);
РеквизитСчет = Группы.Item(2);
РеквизитСумма = Группы.Item(3);
КонецЕсли;
Важно: Вызов COM-объекта регулярных выражений на каждой строке при обработке больших массивов данных (от 10 000 строк) существенно уступает по скорости встроенной функции Сред() из-за накладных расходов межпроцессного взаимодействия.
Если разовая обработка требуется пользователю без написания кода 1С:
Microsoft Text Driver через ADODB.Connection. Для этого рядом с файлом создается конфигурационный файл schema.ini с директивой Format=FixedLength и перечислением параметров Width для каждого столбца, после чего к файлу можно обращаться через стандартный SQL-запрос SELECT * FROM [file.txt].