К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как прочитать двоичные данные в 1С

Как прочитать двоичные данные в 1С: объект ЧтениеДанных, работа с буфером, потоками и файлами. Примеры кода, типичные ошибки и проверка результата.

Двоичные данные в 1С встречаются повсюду: картинки в справочниках, файлы во вложениях, ответы HTTP-сервисов, архивы обменов, подписанные документы. Чтобы извлечь из них информацию — текст, числа, структуру файла — недостаточно просто получить объект ДвоичныеДанные: нужно понимать, как устроены потоки, буферы и механизм последовательного чтения.

Платформа 1С:Предприятие 8.3 предоставляет для этого встроенный объект ЧтениеДанных, который позволяет читать байты, целые числа разной разрядности, строки и отдельные фрагменты из файла или потока. Дополнительно используются объекты Поток, БуферДвоичныхДанных и классические методы ПолучитьИмяВременногоФайла и Записать.

Ниже разобраны основные сценарии: чтение из файла на диске, из хранилища значения, из HTTP-ответа, посимвольный разбор заголовка файла и типичные ошибки, из-за которых данные читаются «криво» или не читаются вовсе.

Что такое двоичные данные в 1С и какие объекты с ними работают

Объект ДвоичныеДанные — это неизменяемый набор байтов. Его можно получить из файла (Новый ДвоичныеДанные(ПутьКФайлу)), из хранилища значения, из HTTP-ответа или из base64-строки. Сам по себе он не даёт инструментов разбора: у него есть только методы Размер() и Записать().

Для пословного разбора содержимого служат три связанных механизма:

  • ЧтениеДанных — последовательное чтение чисел, строк и блоков байтов из файла или потока;
  • Поток / ПотокВПамяти / ФайловыйПоток — универсальное представление источника данных с возможностью чтения и записи;
  • БуферДвоичныхДанных — изменяемый массив байтов с прямым доступом по индексу и указанием порядка байтов.

Выбор инструмента зависит от задачи: если нужно просто переслать или сохранить файл — достаточно ДвоичныеДанные; если нужно разобрать внутреннюю структуру (заголовок, смещения, длины блоков) — потребуется ЧтениеДанных с буфером.

Как получить двоичные данные из файла, хранилища и HTTP

Самый частый источник — файл на диске. Конструктор принимает полный путь и загружает содержимое целиком в память:

ДвоичныеДанные = Новый ДвоичныеДанные("C:\Data\picture.png");

РазмерФайла = ДвоичныеДанные.Размер();

Из хранилища значения (например, реквизит справочника с типом ХранилищеЗначения) данные извлекаются методом Получить():

Данные = ЭлементСправочника.ФайлДанных.Получить();

Если ТипЗнч(Данные) = Тип("ДвоичныеДанные") Тогда

Данные.Записать("C:\Temp\extracted.bin");

КонецЕсли;

Из HTTP-ответа двоичное тело получают методом ПолучитьТелоКакДвоичныеДанные() объекта HTTPОтвет. Это правильный способ для картинок, архивов и PDF — использование ПолучитьТелоКакСтроку() для бинарных ответов испортит данные из-за перекодировки.

Чтение через объект ЧтениеДанных: базовый сценарий

Объект ЧтениеДанных создаётся для файла, потока или объекта ДвоичныеДанные. После открытия доступен указатель текущей позиции (ТекущаяПозиция()), и каждый вызов чтения сдвигает его вперёд.

Чтение = Новый ЧтениеДанных("C:\Data\file.dat");

МагическоеЧисло = Чтение.ПрочитатьЦелое32(); // 4 байта

ДлинаБлока = Чтение.ПрочитатьЦелое16(); // 2 байта

СтрокаИмя = Чтение.ПрочитатьСимволы(8); // 8 символов

Чтение.Закрыть();

Основные методы чтения:

  • ПрочитатьБайт() — один байт как число 0–255;
  • ПрочитатьЦелое16(), ПрочитатьЦелое32(), ПрочитатьЦелое64() — целые числа, по умолчанию в порядке LittleEndian;
  • ПрочитатьСимволы(Количество) — строка указанной длины;
  • ПрочитатьВБуферДвоичныхДанных(Буфер, Позиция, Количество) — блок байтов в буфер;
  • Пропустить(КоличествоБайт) — сдвиг позиции без чтения.

Порядок байтов задаётся либо в конструкторе (Новый ЧтениеДанных(Источник, ПорядокБайтов.BigEndian)), либо свойством ПорядокБайтов до начала чтения. Проверить достижение конца источника можно через ЧтениеЗавершено или сравнением текущей позиции с размером источника.

bsl
Чтение = Новый ЧтениеДанных(ПутьКФайлу);
Пока Не Чтение.ЧтениеЗавершено Цикл
    Байт = Чтение.ПрочитатьБайт();
КонецЦикла;
Чтение.Закрыть();

Работа с потоками и буфером двоичных данных

Когда источник — не файл, а данные в памяти или часть другого потока, удобнее работать через Поток. Из объекта ДвоичныеДанные поток открывается методом ОткрытьПотокДляЧтения(), а для сборки данных в памяти используется ПотокВПамяти.

Поток = ДвоичныеДанные.ОткрытьПотокДляЧтения();

Чтение = Новый ЧтениеДанных(Поток);

Буфер = Новый БуферДвоичныхДанных(16);

Чтение.ПрочитатьВБуферДвоичныхДанных(Буфер);

ПервыйБайт = Буфер[0];

Чтение.Закрыть();

Буфер полезен, когда нужно прочитать блок байтов и затем интерпретировать его по-разному: как массив байтов, как числа с разным порядком следования, как подстроки. У буфера можно указать собственный порядок байтов, отличный от порядка чтения, — это упрощает разбор смешанных форматов.

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

Распространённые ошибки и как их избежать

Большинство проблем при чтении двоичных данных связано не с синтаксисом, а с неверными предположениями о формате источника. Ниже — типичные ситуации.

Типичные ошибки
01
Чтение чисел без указания порядка байтов: на форматах с BigEndian значения получаются «перевёрнутыми» и бессмысленными.
02
Попытка прочитать больше байт, чем осталось в источнике: возникает исключение, которое часто глотают пустой обработкой Попытка/Исключение.
03
Использование ПолучитьТелоКакСтроку для бинарного HTTP-ответа: данные портятся перекодировкой, файл после сохранения не открывается.
04
Обращение к буферу с индекса 1 вместо 0: смещается весь разбор структуры, ошибка проявляется далеко от места причины.
05
Не закрытое чтение или поток: файл остаётся занятым, повторная запись в него невозможна до завершения сеанса.

Отдельная группа ошибок — кодировки строк. Метод ПрочитатьСимволы по умолчанию интерпретирует байты в кодировке UTF-8. Если строка в файле записана в ANSI (Windows-1251) или UTF-16, результат будет искажён. Кодировку задают параметром конструктора или свойством КодировкаТекста до чтения.

Ещё одна ловушка — чтение на клиенте в веб-клиенте или тонком клиенте без доступа к файловой системе сервера. Конструктор Новый ДвоичныеДанные(Путь) ищет файл на той стороне, где выполняется код: в серверной процедуре путь клиентской машины недоступен. Файл нужно сначала передать на сервер через ПоместитьВоВременноеХранилище или механизм НачатьПомещениеФайла.

Как проверить, что данные прочитаны правильно

Надёжный способ верификации — сравнение контрольных признаков формата. У большинства форматов файлов первые байты образуют сигнатуру: у PNG это последовательность 89 50 4E 47, у ZIP — 50 4B 03 04, у PDF — строка %PDF. Прочитайте первые байты и сравните с ожидаемыми: если сигнатура не совпадает, источник повреждён или это не тот формат.

Контрольные точки

Как проверить результат

Сигнатура файла (первые 4–8 байт)
Норма

Совпадает с известной сигнатурой формата

Тревожный признак

Другие байты — источник повреждён или формат иной

Размер прочитанных данных
Норма

Сумма прочитанных блоков равна размеру источника

Тревожный признак

Расхождение — где-то пропущены или лишне прочитаны байты

Сохранённый файл открывается
Норма

Файл после Записать() открывается штатной программой

Тревожный признак

Ошибка открытия — данные испорчены при получении или записи

Прочитанные числа в разумных пределах
Норма

Длины и смещения не превышают размер файла

Тревожный признак

Огромные значения — неверный порядок байтов или смещение чтения

Для отладки полезно выводить прочитанные байты в шестнадцатеричном виде и сверять с дампом файла в любом hex-редакторе. Если структура разбирается циклично (блок «длина + данные»), добавьте проверку, что прочитанная длина не превышает остаток источника, — это защитит от бесконечного цикла на испорченных данных.

Порядок действий при разборе неизвестного формата
  1. 1
    Откройте файл в hex-редакторе и определите сигнатуру и структуру заголовка.
  2. 2
    Создайте ЧтениеДанных с нужным порядком байтов и кодировкой.
  3. 3
    Прочитайте и проверьте сигнатуру — при несовпадении прекратите разбор.
  4. 4
    Последовательно читайте поля, контролируя текущую позицию после каждого шага.
  5. 5
    Проверьте, что суммарно прочитан весь источник, и закройте чтение.

Когда разбор не нужен: передача и сохранение как есть

Во многих прикладных задачах разбирать содержимое не требуется: файл нужно просто сохранить в базу, отправить по HTTP или приложить к письму. В этом случае работайте с ДвоичныеДанные как с «чёрным ящиком»: получили — записали методом Записать(Путь) или поместили в хранилище значения.

Для передачи через текстовые протоколы (JSON, XML) двоичные данные кодируют в base64 функциями Base64Строка() и Base64Значение(). Учтите, что base64 увеличивает объём примерно на треть — для больших файлов это заметно влияет на трафик и время обмена.

Маршрут решения

Как выбрать действие

Нужно сохранить или переслать файл без анализа содержимогоИспользуйте ДвоичныеДанные и метод Записать — разбор не требуется
Нужно извлечь отдельные поля из заголовка или структуры файлаОткройте ЧтениеДанных и читайте поля последовательно с контролем позиции
Файл большой и нужна только его частьИспользуйте ФайловыйПоток с переходом к нужному смещению
Данные передаются в JSON или XMLКодируйте через Base64Строка и декодируйте Base64Значение на приёмной стороне
Итог

Что важно запомнить

ДвоичныеДанные — контейнер байтов без средств разбора; для разбора служит ЧтениеДанных с методами чтения чисел, строк и буферов. Контролируйте порядок байтов, кодировку, текущую позицию и не загружайте большие файлы целиком в память. Перед разбором проверяйте сигнатуру формата, после — открываемость результата.

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

Чем ЧтениеДанных отличается от ЧтениеТекста?

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

Как прочитать двоичные данные из макета конфигурации?

Если макет имеет тип «Двоичные данные», используйте ПолучитьМакет("ИмяМакета") — результатом будет объект ДвоичныеДанные. Дальше его можно записать в файл методом Записать или открыть поток для разбора через ОткрытьПотокДляЧтения.

Можно ли читать файл частями, не загружая его целиком?

Да. Откройте ФайловыйПоток методом ФайловыеПотоки.ОткрытьДляЧтения(Путь), затем создайте ЧтениеДанных для этого потока и читайте блоками фиксированного размера в цикле. Позицию внутри файла задают методом Перейти с указанием смещения.

Почему прочитанные числа выглядят бессмысленно большими?

Чаще всего причина в неверном порядке байтов: данные записаны в BigEndian, а читаются в LittleEndian (или наоборот). Задайте свойство ПорядокБайтов объекта ЧтениеДанных в соответствии с форматом источника и перечитайте значения. Вторая возможная причина — сбитая позиция чтения из-за ошибки в длине предыдущего поля.

Как сохранить картинку из базы на диск?

Получите значение из реквизита типа ХранилищеЗначения методом Получить(), проверьте, что тип результата — ДвоичныеДанные, и вызовите метод Записать с полным путём, включая правильное расширение файла. Если расширение неизвестно, определите формат по сигнатуре первых байтов.

Работает ли ЧтениеДанных на клиенте и в веб-клиенте?

Объект доступен и на клиенте, и на сервере, но файловые операции выполняются на той стороне, где исполняется код. В веб-клиенте прямой доступ к диску пользователя ограничен: файл сначала помещают во временное хранилище через асинхронные методы помещения файлов, а разбор выполняют на сервере.