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

· 9 мин

Содержание

Выгрузка из банка лежит на диске в виде kl_to_1c.txt, и нужно получить её одной строкой, чтобы разобрать регулярным выражением или передать в обработку. В 1С 8.3 для этого достаточно объекта ЧтениеТекста: создаёте его с именем файла и кодировкой, вызываете Прочитать() без параметров и получаете весь текст в переменной. Остальное в этой теме касается кодировки, больших файлов и того, откуда файл вообще пришёл.

Способов несколько, и выбирать нужно по задаче: целиком в память, построчно, из двоичных данных или из потока. Ниже каждый вариант с рабочим кодом.

Какой способ выбрать

Сначала таблица, потом подробности.

СпособКогда подходитОграничения
ЧтениеТекста + Прочитать()Файл небольшой, нужен весь текст одной строкойВесь файл попадает в память
ЧтениеТекста + ПрочитатьСтроку()Большой файл, строки обрабатываются по однойСвоя логика склейки, если нужна одна строка
ТекстовыйДокументНужен доступ к строкам по номеру, правка и запись обратноТоже грузит всё в память
ДвоичныеДанные + ПолучитьСтрокуИзДвоичныхДанныхДанные уже в виде двоичных, например из временного хранилища или вложенияКодировку указываете сами
ЧтениеДанныхСмешанный формат: часть текст, часть числа и байтыДля чистого текста избыточно

Если файл меньше нескольких десятков мегабайт, берите первый вариант. Он короче всех и ошибиться в нём сложно.

Объект ЧтениеТекста: весь файл за один вызов

Базовый пример. Читаем файл целиком и сразу закрываем:

ИмяФайла = "C:\Обмен\kl_to_1c.txt";

Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.ANSI);
Текст = Чтение.Прочитать();
Чтение.Закрыть();

Сообщить("Прочитано символов: " + СтрДлина(Текст));

Что здесь важно:

  1. Вторым параметром конструктора идёт кодировка. Если её не указать, платформа попробует определить сама, и для файлов без метки порядка байтов результат не всегда верный.
  2. Прочитать() без аргументов возвращает всё, что осталось от текущей позиции до конца файла. Если до этого читали строки, получите хвост.
  3. Закрыть() обязателен. Пока объект не закрыт, файл может быть занят, и другая программа не сможет его удалить или перезаписать.

Перенос строк в результате

В полученной строке переводы строк сохраняются как символ Символы.ПС. Если файл создан в Windows и содержит пары CR LF, конструктор нормализует их до одного перевода. Это поведение настраивается параметрами РазделительСтрок и РазделительСтрокВФайле того же конструктора. Для обычных задач менять ничего не нужно.

Если нужно убрать переводы и получить единую строку, подставьте пробел:

ОднаСтрока = СтрЗаменить(Текст, Символы.ПС, " ");

Вариант через ТекстовыйДокумент

Подходит, когда после чтения вы хотите работать со строками по номерам:

Документ = Новый ТекстовыйДокумент;
Документ.Прочитать("C:\Обмен\kl_to_1c.txt", КодировкаТекста.ANSI);

Текст = Документ.ПолучитьТекст();
Количество = Документ.КоличествоСтрок();
Первая = Документ.ПолучитьСтроку(1);

Нумерация строк начинается с единицы. ПолучитьТекст() склеивает все строки обратно в одну переменную с разделителями.

Построчное чтение и сборка в одну строку

Для объёмных логов и выгрузок читать целиком невыгодно. ПрочитатьСтроку() возвращает очередную строку и Неопределено, когда файл закончился:

Чтение = Новый ЧтениеТекста("C:\Обмен\big_log.txt", КодировкаТекста.UTF8);

Счётчик = 0;
Строка = Чтение.ПрочитатьСтроку();
Пока Строка <> Неопределено Цикл
    Если СтрНачинаетсяС(Строка, "ERROR") Тогда
        Счётчик = Счётчик + 1;
    КонецЕсли;
    Строка = Чтение.ПрочитатьСтроку();
КонецЦикла;

Чтение.Закрыть();
Сообщить("Строк с ошибками: " + Счётчик);

Если всё же нужна одна строка, но с фильтрацией, собирайте результат не конкатенацией в цикле, а через массив и СтрСоединить:

Части = Новый Массив;
Строка = Чтение.ПрочитатьСтроку();
Пока Строка <> Неопределено Цикл
    Если НЕ ПустаяСтрока(Строка) Тогда
        Части.Добавить(СокрЛП(Строка));
    КонецЕсли;
    Строка = Чтение.ПрочитатьСтроку();
КонецЦикла;

Результат = СтрСоединить(Части, " ");

Разница заметна на десятках тысяч строк. Склейка Результат = Результат + Строка каждый раз создаёт новую строку, а массив накапливает куски и собирает их одним вызовом.

Кодировки и двоичные данные

Самая частая причина «кракозябр» после чтения: файл в одной кодировке, а читается в другой. Банковские выгрузки и старые обмены обычно идут в windows-1251, современные файлы в UTF-8.

Что видите в текстеВероятная причинаЧто поставить
СпаÑ...Файл в UTF-8, прочитан как ANSIКодировкаТекста.UTF8
ПриветТекст в UTF-8 уже был ошибочно прочитан как ANSI и сохранён зановоБрать исходный файл и читать как КодировкаТекста.UTF8
Символы ? вместо буквФайл в DOS-кодировкеКодировкаТекста.OEM
Лишний символ в начале первой строкиМетка порядка байтов (BOM)Читать как КодировкаТекста.UTF8

Кодировку можно передать и строкой, например "windows-1251". Это удобно, когда название приходит из настроек обмена.

Чтение из двоичных данных

Файл можно сначала загрузить как ДвоичныеДанные, а потом превратить в строку с явной кодировкой:

Данные = Новый ДвоичныеДанные("C:\Обмен\kl_to_1c.txt");
Текст = ПолучитьСтрокуИзДвоичныхДанных(Данные, КодировкаТекста.ANSI);

Это ровно тот случай, когда файла на диске уже нет: данные пришли из реквизита, из вложения письма или из временного хранилища. Обратное действие делает ПолучитьДвоичныеДанныеИзСтроки.

ЧтениеДанных для смешанных файлов

Если в файле перемешаны текст и двоичные поля, подойдёт ЧтениеДанных. Он читает из имени файла, двоичных данных или потока и умеет вытаскивать строки с указанной кодировкой. Для файла, где только текст, он ничего не даёт: ЧтениеТекста короче и понятнее.

Если файл пришёл из временного хранилища или потока

Пользователь загрузил файл через форму, и на сервер он попал как адрес во временном хранилище. Файла на диске сервера нет, есть только адрес:

&НаСервере
Функция ПрочитатьФайлКакСтроку(АдресХранилища, Кодировка)
    Данные = ПолучитьИзВременногоХранилища(АдресХранилища);
    Возврат ПолучитьСтрокуИзДвоичныхДанных(Данные, Кодировка);
КонецФункции

Проверьте, что в хранилище действительно лежат двоичные данные, а не значение другого типа. Если вы сами помещали туда файл через механизм загрузки, это так и будет.

Второй вариант: сохранить данные во временный файл и прочитать обычным способом. Он нужен, если библиотека принимает только путь:

ИмяВремФайла = ПолучитьИмяВременногоФайла("txt");
Данные.Записать(ИмяВремФайла);

Чтение = Новый ЧтениеТекста(ИмяВремФайла, КодировкаТекста.UTF8);
Текст = Чтение.Прочитать();
Чтение.Закрыть();

УдалитьФайлы(ИмяВремФайла);

Временный файл нужно удалять самому. Иначе каталог временных файлов на сервере будет расти, особенно при регулярной загрузке.

Чтение из потока

ЧтениеТекста умеет работать и с потоком, не только с путём:

Поток = Новый ФайловыйПоток("C:\Обмен\kl_to_1c.txt",
    РежимОткрытияФайла.Открыть, ДоступКФайлу.Чтение);

Чтение = Новый ЧтениеТекста(Поток, КодировкаТекста.ANSI);
Текст = Чтение.Прочитать();
Чтение.Закрыть();
Поток.Закрыть();

Поток закрывается отдельно. Это удобно, когда один и тот же поток нужен ещё и для других операций, например для расчёта контрольной суммы.

Что делать со строкой после чтения

Сам по себе текст в переменной редко нужен. Обычно его режут на части или ищут в нём значения. Три приёма закрывают большинство задач.

Разбить на строки или поля. СтрРазделить возвращает массив, второй параметр задаёт разделитель:

Строки = СтрРазделить(Текст, Символы.ПС, Ложь);
Для Каждого Строка Из Строки Цикл
    Поля = СтрРазделить(Строка, ";", Истина);
    Если Поля.Количество() >= 3 Тогда
        Сообщить("Контрагент: " + Поля[0] + ", сумма: " + Поля[2]);
    КонецЕсли;
КонецЦикла;

Третий параметр Ложь отбрасывает пустые элементы, а в вызове для полей оставлен Истина: пустое поле в CSV занимает своё место, и если его выбросить, индексы поедут.

Найти нужный фрагмент. СтрНайти(Текст, "ИНН=") возвращает позицию первого вхождения или ноль, если подстроки нет. Дальше Сред(Текст, Позиция + 4, 12) вырезает значение.

Сверить размер. До чтения полезно узнать, с чем имеете дело:

Файл = Новый Файл(ИмяФайла);
Если Файл.Существует() И Файл.Размер() > 50 * 1024 * 1024 Тогда
    // файл больше 50 МБ, читаем построчно
КонецЕсли;

Порог в 50 МБ условный. Ориентируйтесь на память вашего сервера и число одновременных сеансов: один такой файл переживёт любой сервер, а двадцать параллельных загрузок уже нет.

Записать обратно

Если текст изменили и нужно сохранить, используйте ЗаписьТекста с тем же набором кодировки, в которой файл будет читать следующая программа:

Запись = Новый ЗаписьТекста("C:\Обмен\result.txt", КодировкаТекста.UTF8);
Запись.Записать(Текст);
Запись.Закрыть();

Для банковских клиентов и старых систем чаще нужна ANSI. Узнайте это у принимающей стороны, а не подбирайте наугад: файл с неверной кодировкой они примут молча и покажут мусор.

Ошибки при чтении: что перехватывать

Читать файл без защиты можно только в отладочных обработках. В рабочем коде оберните чтение в Попытка:

Текст = "";
Чтение = Неопределено;
Попытка
    Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8);
    Текст = Чтение.Прочитать();
Исключение
    Сообщить("Не удалось прочитать файл: " +
        КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;

Если Чтение <> Неопределено Тогда
    Чтение.Закрыть();
КонецЕсли;

Чаще всего падает по трём причинам:

  • 📁 Файла нет или путь указан неверно. Проверьте заранее: Новый Файл(ИмяФайла).Существует().
  • 🔒 Файл занят другой программой. Например, открыт в Excel или ещё пишется обменом. Подождите или читайте копию.
  • 🚫 Нет прав на каталог. На сервере чтение идёт от имени пользователя, под которым запущена служба сервера 1С, а не от имени пользователя, нажавшего кнопку.

Последний пункт путают чаще остальных. Путь C:\Обмен на клиентском компьютере и на сервере — разные каталоги. Если код выполняется &НаСервере, файл должен быть доступен серверу, чаще всего по сетевому пути.

Диагностика: выберите симптом

Большие файлы: как не съесть всю память

Загрузка файла на 500 МБ одной строкой — гарантированная нагрузка на память сервера. Строка в 1С хранится в юникоде, поэтому занимает в памяти примерно вдвое больше, чем весь файл в однобайтовой кодировке. 100 МБ ANSI превращаются примерно в 200 МБ строки, плюс копии при каждой операции над ней.

Что делать, если файл большой:

  1. Читать порциями. ПрочитатьСтроку() держит в памяти одну строку, а не файл целиком.
  2. Не склеивать в цикле. Используйте массив и СтрСоединить, как показано выше.
  3. Обрабатывать на лету. Разбирайте строку сразу после чтения и пишите результат в таблицу значений или в базу, а не копите исходный текст.
  4. Читать на сервере. Клиентское приложение при больших объёмах упирается в передачу данных между клиентом и сервером.
  5. Закрывать объект сразу. Не держите ЧтениеТекста открытым дольше необходимого.

Если нужна только часть файла, например первые 10 строк для проверки формата, не читайте всё:

Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8);
Для Номер = 1 По 10 Цикл
    Строка = Чтение.ПрочитатьСтроку();
    Если Строка = Неопределено Тогда
        Прервать;
    КонецЕсли;
    Сообщить(Строка);
КонецЦикла;
Чтение.Закрыть();

Замерить время можно через ТекущаяУниверсальнаяДатаВМиллисекундах() до и после чтения. На типичном сервере файл в несколько мегабайт читается за доли секунды, и оптимизировать там нечего. Беспокоиться стоит от сотен мегабайт.

Частые вопросы

Чем `Прочитать()` отличается от `ПрочитатьСтроку()`?
Прочитать() возвращает весь оставшийся текст одной строкой. ПрочитатьСтроку() возвращает одну строку за вызов и Неопределено, когда файл закончился.
Нужно ли закрывать `ЧтениеТекста`?
Да. Без Закрыть() файл остаётся занятым, и его нельзя удалить или перезаписать до завершения сеанса.
Как прочитать файл в UTF-8 с BOM?
Укажите КодировкаТекста.UTF8. Метку порядка байтов платформа распознаёт и в текст не включает.
Можно ли прочитать файл на клиенте в веб-клиенте?
Напрямую по пути нет: браузер не отдаёт доступ к диску. Файл выбирается пользователем, помещается на сервер через временное хранилище и читается оттуда.
Что быстрее: `ТекстовыйДокумент` или `ЧтениеТекста`?
ЧтениеТекста обычно экономнее, потому что не хранит строки в виде списка. ТекстовыйДокумент удобнее, если нужны номера строк и правка.