При интеграции 1С с банковскими системами программисты часто сталкиваются с ситуацией, когда файл выгрузки, визуально выглядящий корректно в обычном блокноте, «разваливается» при попытке прочитать его программно. Проблема заключается в скрытых символах переноса строки, которые нарушают логическую структуру данных. В этой статье мы подробно разберем, почему это происходит, как диагностировать проблему и какими программными способами можно очистить файл перед загрузкой.
В операционной системе Windows стандартом окончания строки является пара символов CRLF (возврат каретки — CR, код 13, и перевод строки — LF, код 10). Однако многие банковские сервера работают под управлением Unix-подобных систем, где разделителем является только одиночный символ LF.
Ситуация осложняется, когда внутри текстовых полей (например, в адресе контрагента или в назначении платежа) появляются «одиночные» символы перевода строки. Рассмотрим пример: банк выгружает адрес, где после названия города стоит символ LF. При использовании стандартного метода ПрочитатьСтроку(), система 1С воспримет этот символ как конец текущей записи, и следующая часть адреса попадет в новую итерацию чтения, что приведет к ошибке разбора (парсинга) данных.
Прежде чем приступать к написанию кода, нам необходимо точно выяснить, какой именно символ мешает загрузке. Проанализируем файл с помощью текстового редактора Notepad++:
¶).[CR][LF], а в середине данных — только [LF], значит, мы нашли причину сбоя.ANSI или UTF-8).Самый простой и эффективный способ для файлов небольшого и среднего размера — это чтение всего текста в одну переменную и замена «плохих» переносов. Однако нельзя просто удалить все LF, так как мы удалим и нужные разделители строк. Выясним, как сделать это правильно с помощью алгоритма временного маркера.
Рассмотрим пример кода:
// Считываем весь файл в строку
ТекстДокумента = ПолучитьТекстИзФайла(ПутьКФайлу);
// 1. Заменяем правильные переносы строк на уникальный маркер, который точно не встретится в тексте
Маркер = "###МАРКЕР_СТРОКИ###";
ТекстДокумента = СтрЗаменить(ТекстДокумента, Символы.ВК + Символы.ПС, Маркер);
// 2. Теперь удаляем все оставшиеся одиночные символы переноса
ТекстДокумента = СтрЗаменить(ТекстДокумента, Символы.ПС, " "); // Заменяем LF на пробел
ТекстДокумента = СтрЗаменить(ТекстДокумента, Символы.ВК, " "); // Заменяем CR на пробел (на всякий случай)
// 3. Возвращаем правильные переносы строк обратно
ТекстДокумента = СтрЗаменить(ТекстДокумента, Маркер, Символы.ВК + Символы.ПС);
Этот метод гарантирует, что структура файла сохранится, а лишние разрывы внутри полей (например, в адресе Вася Г. МИНСК) превратятся в обычные пробелы.
Если в вашем файле данные разделены специальным символом (в нашем случае автор указал символ $), мы можем использовать более современный подход. Функция СтрРазделить позволяет игнорировать физические переносы строк, если мы точно знаем структуру документа.
Разберем по шагам, как это реализовать:
// Читаем весь текст файла
ЧтениеТекста = Новый ЧтениеТекста(ПутьКФайлу, КодировкаТекста.ANSI);
ПолныйТекст = ЧтениеТекста.Прочитать();
ЧтениеТекста.Закрыть();
// Разделяем текст в массив по символу "$"
МассивДанных = СтрРазделить(ПолныйТекст, "$", Истина);
// Теперь в МассивДанных[2] может содержаться текст с переносом "ИП Вася\nМИНСК",
// но он не разрывает логику обработки остальных полей.
// При необходимости внутри каждого элемента массива можно удалить ПС:
Для Каждого Элемент Из МассивДанных Цикл
ОчищенноеЗначение = СтрЗаменить(Элемент, Символы.ПС, " ");
// Далее работаем с очищенными данными
КонецЦикла;
В ситуациях, когда файл очень большой (десятки или сотни мегабайт), загрузка его целиком в память в виде строки может вызвать ошибку Нехватка памяти. В таких случаях проанализируем файл как поток байтов. Нам нужно найти байт с кодом 10 (LF) и проверить, стоит ли перед ним байт 13 (CR).
Посмотрим на пример реализации:
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
ЧтениеДанных = Новый ЧтениеДанных(ДвоичныеДанные);
Буфер = ЧтениеДанных.Прочитать(); // Читаем всё или порциями
ЧтениеДанных.Закрыть();
НовыйБуфер = Новый Массив;
ПредыдущийБайт = 0;
Для Каждого ТекущийБайт Из Буфер Цикл
Если ТекущийБайт = 10 Тогда // Это LF
Если ПредыдущийБайт <> 13 Тогда // Перед ним нет CR
// Это "плохой" перенос. Заменим его на байт пробела (код 32)
НовыйБуфер.Добавить(32);
Иначе
НовыйБуфер.Добавить(ТекущийБайт);
КонецЕсли;
Иначе
НовыйБуфер.Добавить(ТекущийБайт);
КонецЕсли;
ПредыдущийБайт = ТекущийБайт;
КонецЦикла;
// Сохраняем очищенный файл
РезультатДанные = Новый ДвоичныеДанные(НовыйБуфер);
РезультатДанные.Записать(ПутьКФайлуОчищенный);
Чтобы каждый раз не править код при изменении формата банка, рекомендуется реализовать механизм предварительной «нормализации» файла. Создайте отдельную функцию НормализоватьФайлЗагрузки(), которая будет вызываться перед основным алгоритмом обработки.
Важные моменты:
UTF-8 с меткой BOM, а вы читаете его как ANSI, функции поиска символов могут работать некорректно.Попытка...Исключение при чтении файлов, так как файл может быть занят другим процессом (например, самим клиент-банком в момент выгрузки).VBScript.RegExp через COM-объект, вы можете удалять переносы более точечно, например, только те, после которых не следует дата в формате ДД.ММ.ГГГГ.Таким образом, мы выяснили, что основной причиной сбоя загрузки являются невидимые символы перевода строки LF. Использование метода предварительной замены через маркер или работа с текстом как с массивом по разделителю $ позволяют полностью решить проблему без необходимости обращения в техническую поддержку банка.