Как разобрать текстовый файл без разделителей с фиксированной длиной полей в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С со сторонними системами (банковскими шлюзами, терминалами, устаревшим учетным ПО) часто приходится сталкиваться с текстовыми файлами формата Fixed-width (Flat file). В таких файлах отсутствуют привычные символы-разделители (запятая, точка с запятой, символ табуляции), а все данные идут сплошной строкой. Каждая колонка имеет строго регламентированную длину, а короткие значения дополняются пробелами или ведущими нулями.

Разберем основные и наиболее эффективные способы чтения и разбора таких данных в 1С:Предприятие.

Способ 1. Построчное чтение и нарезка функцией Сред()

Для файлов любого объема (включая десятки и сотни тысяч строк) наиболее быстрым и нетребовательным к оперативной памяти является использование объекта ЧтениеТекста в сочетании со встроенной строковой функцией Сред().

Рассмотрим порядок разбора по шагам:

  1. Открываем файл для построчного чтения с явным указанием кодировки (например, КодировкаТекста.OEM для DOS-файлов или системной кодовой страницы CP866).
  2. В цикле считываем файл методом ПрочитатьСтроку() до конца файла (пока значение не равно Неопределено).
  3. Выделяем фрагменты строки по их начальной позиции и длине с помощью функции Сред().
  4. Очищаем текстовые поля от хвостовых пробелов функцией СокрЛП().

Посмотрим на базовый пример реализации:


ПутьКФайлу = "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));
                Значение = Дата(Год, Месяц, День);
            Иначе
                Значение = ЗначениеСтр;
            КонецЕсли;
            
            ДанныеСтроки.Вставить(Настройка.ИмяПоля, Значение);
        КонецЦикла;
        
        // ДанныеСтроки содержит готовую структуру с типизированными значениями
    КонецЕсли;
    
    СтрокаФайла = ТекстФайл.ПрочитатьСтроку();
КонецЦикла;

ТекстФайл.Закрыть();

Особенности работы с кодировками, датами и суммами

При разборе фиксированных форматов важно учитывать следующие технические нюансы:

  1. Кодировка и смещение байтов: Большинство спецификаций фиксированных форматов разрабатывались под однобайтовые кодировки (CP866 или Windows-1251). При открытии таких файлов в многобайтовой кодировке (например, UTF-8) русские буквы занимают по 2 байта, что приведет к сдвигу всех последующих колонок при посимвольном чтении. Всегда указывайте точную кодовую страницу источника.
  2. Суммы в копейках без точки: Во многих межбанковских файлах суммы выгружаются целым числом в минимальных денежных единицах (например, строка 0000015000 соответствует сумме 150.00). Для корректного получения значения строку необходимо преобразовать через функцию Число() и разделить на 100.
  3. Слитные даты: Даты часто представлены как ГГГГММДД или ДДММГГГГ. Для их преобразования выделяйте год, месяц и день отдельными вызовами Сред() и передавайте в конструктор Дата(Год, Месяц, День).

Способ 2. Использование регулярных выражений (RegExp)

Если формат строки допускает вариативность или разбор выполняется на платформе 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 строк) существенно уступает по скорости встроенной функции Сред() из-за накладных расходов межпроцессного взаимодействия.

Способ 3. Импорт через Microsoft Excel или ADO

Если разовая обработка требуется пользователю без написания кода 1С:

  1. В приложении Excel можно открыть текстовый файл через Мастер импорта текстов, выбрав режим С фиксированной шириной полей. Мастер позволяет визуально расставить линии разделения колонок и сохранить результат в формате таблицы.
  2. Для программной загрузки средствами СУБД можно использовать драйвер Microsoft Text Driver через ADODB.Connection. Для этого рядом с файлом создается конфигурационный файл schema.ini с директивой Format=FixedLength и перечислением параметров Width для каждого столбца, после чего к файлу можно обращаться через стандартный SQL-запрос SELECT * FROM [file.txt].
← На главную