Двоичные данные в 1С:Предприятие — это тема, которая пугает многих разработчиков из-за кажущейся сложности. На практике же работа с бинарными потоками, файлами в формате .bin, изображениями или архивами требуется чаще, чем кажется: от загрузки прайс-листов поставщиков до интеграции с оборудованием. Главная проблема — отсутствие встроенных "человекопонятных" инструментов для визуализации таких данных. Но как только вы поймёте базовые принципы, двоичные операции станут таким же обыденным делом, как работа с реквизитами справочников.

В этой статье мы разберём все ключевые сценарии: от простого чтения файла до сложной обработки потоков с использованием ДвоичныеДанные, ПотокВПамяти и ЗаписьДанных. Особое внимание уделим типовым ошибкам, которые приводят к "битым" файлам или падению производительности. Если вы когда-нибудь сталкивались с сообщением "Неверный формат потока" или "Ошибка чтения двоичных данных", после прочтения вы будете знать, как их избежать.

Для начинающих программистов работа с двоичными данными часто ассоциируется с низкоуровневым программированием — мол, это "не для нас". На самом деле, в 90% случаев достаточно знания трёх объектов платформы: ДвоичныеДанные, Поток и ЗаписьДанных. А для остальных 10% есть готовые библиотеки от сообщества, о которых мы тоже расскажем. Главное — понимать, что двоичные данные это не "магия", а просто последовательность байтов, которую нужно правильно интерпретировать.

Прежде чем погружаться в код, запомните: двоичные данные в 1С не имеют "типа" в привычном понимании. Это просто "сырые" байты, которые могут означать что угодно — от JPEG-изображения до сериализованного объекта .NET. Ваша задача — знать, как их прочитать, модифицировать и сохранить без искажений. Начнём с самого простого.

1. Основные объекты для работы с двоичными данными

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

Самый простой объект — ДвоичныеДанные. Он представляет собой неизменяемый массив байтов и создаётся либо из файла, либо из строки в кодировке Base64. Пример:


Данные = Новый ДвоичныеДанные("C:\temp\file.bin");

// Или из Base64

Данные = Новый ДвоичныеДанные("SGVsbG8gV29ybGQh", КодировкаТекста.Base64);

Для динамической работы с данными используются потоки:

  • 📥 ПотокЧтения — читает данные из источника (файла, памяти, HTTP-ответа).
  • 📤 ПотокЗаписи — записывает данные в приёмник (файл, память, сетевое соединение).
  • 🔄 ПотокВПамяти — универсальный поток, работающий с данными в оперативной памяти.

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


Запись = Новый ЗаписьДанных();

Запись.ЗаписатьДата(ТекущаяДата());

Запись.ЗаписатьСтроку("Пример текста");

Запись.Закрыть();

Данные = Запись.ПолучитьДвоичныеДанные();

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

📊 Какой объект вы используете чаще всего для работы с двоичными данными?
ДвоичныеДанные
ПотокВПамяти
ЗаписьДанных/ЧтениеДанных
Собственные функции

2. Чтение и запись двоичных файлов

Самая распространённая задача — прочитать файл в двоичном формате или сохранить данные на диск. Здесь

Пример чтения файла в переменную типа ДвоичныеДанные:


ПутьКФайлу = "C:\temp\document.pdf";

Если НЕ ЗначениеЗаполнено(ПутьКФайлу) Тогда

Возврат Ложь;

КонецЕсли;

ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);

// Проверяем, что файл прочитан

Если ДвоичныеДанныеФайла.Размер() = 0 Тогда

Сообщить("Файл пуст или не найден!");

КонецЕсли;

Для записи данных в файл удобно использовать ПотокЗаписи:


ПутьДляСохранения = "C:\temp\output.dat";

Поток = Новый ПотокЗаписи(ПутьДляСохранения);

// Записываем строку в UTF-8

Поток.ЗаписатьСтроку("Привет, мир!", КодировкаТекста.UTF8);

// Записываем число типа Int32

Поток.ЗаписатьЧисло(12345, 4);

// Закрываем поток (обязательно!)

Поток.Закрыть();

Обратите внимание на второй параметр метода ЗаписатьЧисло() — он указывает количество байт для записи. Для Int32 это 4 байта, для Int16 — 2 байта. Если указать неверное значение, число будет записано некорректно!

💡

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

3. Работа с потоками в памяти

ПотокВПамяти — это универсальный инструмент для манипуляции данными без привязки к файловой системе. Он полезен, когда нужно:

  • 🧩 Собрать двоичные данные из нескольких источников.
  • 🔄 Преобразовать формат "на лету" (например, конвертировать изображение).
  • 📤 Подготовить данные для отправки по HTTP.

Пример создания потока и записи в него данных разных типов:


Поток = Новый ПотокВПамяти();

// Записываем заголовок (2 байта: сигнатура 0xABCD)

Поток.ЗаписатьЧисло(0xABCD, 2);

// Записываем версию формата (1 байт)

Поток.ЗаписатьЧисло(1, 1);

// Записываем строку фиксированной длины (20 байт, дополняя пробелами)

Строка = "Тестовые данные";

Поток.ЗаписатьСтроку(Строка + СтрокаНовойСтроки + СтрПовтор(" ", 20 - СтрДлина(Строка)));

// Получаем итоговые двоичные данные

Данные = Поток.ЗакрытьИПолучитьДвоичныеДанные();

Для чтения из потока в памяти используйте ПотокЧтения с конструктором Новый ПотокЧтения(ДвоичныеДанные). Например, так можно распарсить заголовок файла:


Данные = Новый ДвоичныеДанные("C:\temp\header.bin");

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

// Читаем сигнатуру (2 байта)

Сигнатура = Поток.ПрочитатьЧисло(2);

Если Сигнатура <> 0xABCD Тогда

ВызватьИсключение "Неверный формат файла: неверная сигнатура";

КонецЕсли;

Что будет, если прочитать больше байт, чем доступно в потоке?

Методы чтения (например, ПрочитатьЧисло()) вызовут исключение, если в потоке недостаточно данных. Всегда проверяйте размер потока перед чтением или используйте конструкцию Попытка..Исключение.

4. Обработка изображений и мультимедиа

Изображения, аудио и видео файлы — это тоже двоичные данные, но с особенностями. В для работы с ними есть специализированные объекты: Картинка, ФайлКартинки и методы для манипуляции пикселями. Однако часто требуется низкоуровневая обработка, например, для:

  • 🖼️ Извлечения EXIF-меток из фотографий.
  • 🔍 Проверки целостности файлов по сигнатуре.
  • 📦 Упаковки нескольких изображений в один контейнер.

Пример чтения сигнатуры JPEG-файла (первые 2 байта должны быть 0xFFD8):


Функция ЭтоJPEG(ПутьКФайлу)

Данные = Новый ДвоичныеДанные(ПутьКФайлу);

Если Данные.Размер() < 2 Тогда

Возврат Ложь;

КонецЕсли;

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

Сигнатура = Поток.ПрочитатьЧисло(2);

Возврат Сигнатура = 0xFFD8;

КонецФункции

Для извлечения метаданных из изображений можно использовать внешние компоненты (например, AddIn на C#), но простейшие операции доступны и на чистой . Например, так можно получить размеры PNG-файла (сигнатура 0x89504E47, ширина и высота хранятся на смещении 16 байт):


Функция ПолучитьРазмерыPNG(ДвоичныеДанныеФайла)

Если ДвоичныеДанныеФайла.Размер() < 24 Тогда

Возврат Неопределено;

КонецЕсли;

Поток = Новый ПотокЧтения(ДвоичныеДанныеФайла);

// Пропускаем заголовок (8 байт) и chunk IHDR (12 байт)

Поток.Пропустить(20);

Ширина = Поток.ПрочитатьЧисло(4);

Высота = Поток.ПрочитатьЧисло(4);

Возврат Новый Структура("Ширина, Высота", Ширина, Высота);

КонецФункции

Проверьте сигнатуру файла (первые 2-8 байт)|Убедитесь, что размер файла соответствует заявленному|Проверьте CRC/контрольные суммы (если поддерживаются форматом)|Используйте Попытка..Исключение для обработки ошибок-->

5. Сериализация и десериализация данных

Объекты ЗаписьДанных и ЧтениеДанных предназначены для преобразования структурированных данных в двоичный формат и обратно. Это аналог BinaryFormatter в .NET или pickle в Python, но с ограничениями . Главное правило: формат данных должен быть согласован между записью и чтением.

Пример сериализации массива структур:


ДанныеДляЗаписи = Новый Массив;

ДанныеДляЗаписи.Добавить(Новый Структура("Имя, Возраст", "Иван", 30));

ДанныеДляЗаписи.Добавить(Новый Структура("Имя, Возраст", "Мария", 25));

Запись = Новый ЗаписьДанных();

Запись.ЗаписатьМассив(ДанныеДляЗаписи);

ДвоичныеДанные = Запись.Закрыть();

ДвоичныеДанные.Записать("C:\temp\people.dat");

Для десериализации используем ЧтениеДанных:


Данные = Новый ДвоичныеДанные("C:\temp\people.dat");

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

ВосстановленныеДанные = Чтение.ПрочитатьМассив();

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

Важные нюансы:

  • 🔄 Порядок записи и чтения данных должен совпадать.
  • 📏 Размерность чисел (например, ЗаписатьЧисло(100, 4) vs ПрочитатьЧисло(2)) должна быть одинаковой.
  • 🚫 Не все типы данных поддерживаются (например, Соответствие или ДвоичныеДанные внутри структур).
💡

Для обмена данными между разными системами лучше использовать универсальные форматы (JSON, XML, Protobuf) вместо двоичной сериализации 1С. Она предназначена для внутреннего использования в пределах одной платформы.

6. Типовые ошибки и их решение

Работа с двоичными данными чревата ошибками, которые сложно диагностировать. Вот наиболее распространённые проблемы и способы их решения:

Ошибка Причина Решение
Неверный формат потока Попытка прочитать данные неверного типа (например, число вместо строки). Проверьте порядок и типы данных при записи/чтении.
Недостаточно данных в потоке Попытка прочитать больше байт, чем доступно. Используйте Поток.Доступно() перед чтением.
Файл сохраняется "битым" Поток не закрыт или данные записаны в неправильной кодировке. Всегда закрывайте потоки и проверяйте кодировки строк.
Ошибка доступа к файлу Файл заблокирован другой программой или недостаточно прав. Проверьте права доступа и используйте Попытка..Исключение.

Особенно коварна ошибка "Неверный формат потока" при работе с ЧтениеДанных. Она возникает, если порядок или типы данных при записи и чтении не совпадают. Например:


// Ошибка: записали число как Int32 (4 байта), а читаем как Int16 (2 байта)

Запись.ЗаписатьЧисло(1000, 4);

..

Число = Чтение.ПрочитатьЧисло(2); // Исключение!

Ещё одна частая проблема — неверная интерпретация строк. По умолчанию использует кодировку UTF-16 (2 байта на символ), но многие внешние системы ожидают UTF-8 или Windows-1251. Всегда явно указывайте кодировку:


Поток.ЗаписатьСтроку("Привет", КодировкаТекста.UTF8);

💡

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

7. Продвинутые сценарии: шифрование, сжатие, работа с оборудованием

Для сложных задач стандартных средств может не хватить. В таких случаях используют:

  • 🔐 Шифрование: внешние компоненты (например, CryptoPro или OpenSSL через AddIn).
  • 🗜️ Сжатие: алгоритмы ZIP/GZIP через COM-объекты или HTTP-Сервисы.
  • 🖨️ Работа с оборудованием: прямая отправка команд на принтеры, сканеры или весы в двоичном формате.

Пример сжатия данных с использованием COM-объекта (требует установленного 7-Zip):


Попытка

Zip = Новый COMОбъект("7zip.7z");

ВходнойФайл = "C:\temp\data.bin";

Архив = "C:\temp\data.zip";

// Добавляем файл в архив

Zip.AddToArchive(Архив, ВходнойФайл);

Исключение

Сообщить("Ошибка сжатия: " + ОписаниеОшибки());

КонецПопытки;

Для работы с оборудованием часто требуется отправлять двоичные команды по COM-порту или TCP/IP. Пример отправки команды на фискальный регистратор:


Порт = Новый COMОбъект("MSCOMMLib.MSComm");

Порт.CommPort = 1; // COM1

Порт.Settings = "9600,N,8,1";

Порт.PortOpen = Истина;

// Команда на печать текста (пример для ЕГАИС)

Команда = Новый ПотокВПамяти();

Команда.ЗаписатьЧисло(0x1B, 1); // ESC

Команда.ЗаписатьЧисло(0x40, 1); // @ (сброс принтера)

Команда.ЗаписатьСтроку("ТЕСТ", КодировкаТекста.CP866);

Порт.Output = Команда.ЗакрытьИПолучитьДвоичныеДанные().ПолучитьБуфер();

💡

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

8. Оптимизация производительности

Работа с двоичными данными может тормозить систему, если не следить за:

  • 🗑️ Утечками памяти: не закрытые потоки или большие массивы в памяти.
  • 🐢 Медленными операциями: чтение/запись мелких порций данных в цикле.
  • 🔄 Лишними преобразованиями: многократное кодирование/dekодирование Base64.

Примеры оптимизации:

  1. Используйте ПотокВПамяти для промежуточных операций вместо создания множества объектов ДвоичныеДанные.
  2. При работе с большими файлами читайте/пишите данные блоками по 4096–8192 байт:
    
    

    РазмерБлока = 8192;

    ПотокЧтения = Новый ПотокЧтения(ИсходныйФайл);

    ПотокЗаписи = Новый ПотокЗаписи(ЦелевойФайл);

    Пока ПотокЧтения.Доступно() > 0Loop

    Данные = ПотокЧтения.Прочитать(Min(ПотокЧтения.Доступно(), РазмерБлока));

    ПотокЗаписи.Записать(Данные);

    КонецЦикла;

  3. Избегайте Base64 для хранения больших двоичных данных в базе — используйте ХранилищеЗначения.

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


ФоновоеЗадание = ФоновыеЗадания.Создать("ОбработкаФайла",

Новый Структура("ПутьКФайлу", "C:\temp\large.bin"),

Новый ОписаниеОповещения("ЗавершениеОбработки", ЭтотОбъект));

ФоновоеЗадание.ВыполнитьАсинхронно();

💡

Для файлов размером более 100 МБ используйте потоковую обработку (чтение/запись блоками) вместо загрузки всего файла в память.

⚠️ Внимание: При работе с большими двоичными данными в веб-клиенте учитывайте ограничения на размер передаваемых данных (по умолчанию — 10 МБ). Для обхода ограничений используйте chunked-загрузку или внешние хранилища.

FAQ: Частые вопросы по работе с двоичными данными

Как преобразовать двоичные данные в строку Base64?

Используйте метод Base64Строка() объекта ДвоичныеДанные:

СтрокаBase64 = ДвоичныеДанные.Base64Строка();

Обратное преобразование:

ДвоичныеДанные = Новый ДвоичныеДанные(СтрокаBase64, КодировкаТекста.Base64);
Можно ли сравнить два двоичных файла на идентичность?

Да, сравните их ХэшMD5 или ХэшSHA1:


Хэш1 = ДвоичныеДанные1.ХэшMD5();

Хэш2 = ДвоичныеДанные2.ХэшMD5();

Если Хэш1 = Хэш2 Тогда

// Файлы идентичны

КонецЕсли;

Или сравните байты напрямую (для небольших файлов):

Результат = ДвоичныеДанные1.Сравнить(ДвоичныеДанные2);
Как извлечь двоичные данные из HTTP-ответа?

Используйте ПотокЧтения из объекта HTTPСоединение:


HTTP = Новый HTTPСоединение("example.com");

Ответ = HTTP.Получить("/file.bin");

ДвоичныеДанные = Ответ.ПолучитьТелоКакДвоичныеДанные();

Для больших файлов читайте ответ потоково:


Поток = Ответ.ПолучитьТелоКакПоток();

Поток.СохранитьКак("C:\temp\downloaded.bin");

Почему при записи числа в поток оно сохраняется "неправильно"?

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

Поток.ЗаписатьЧисло(12345, 4); // Правильно

Поток.ЗаписатьЧисло(12345, 2); // Ошибка: число будет усечено!

Также проверьте порядок байт (endianness): по умолчанию использует Little-Endian.

Как работать с двоичными данными в управляемых формах?

Для отображения двоичных данных (например, изображений) используйте реквизит типа ХранилищеЗначения или ДвоичныеДанные с привязкой к элементу формы ПолеДвоичныхДанных:


&НаКлиенте

Процедура ЗагрузитьФайл(Команда)

Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);

Если Диалог.Выбрать() Тогда

Данные = Новый ДвоичныеДанные(Диалог.ПолноеИмяФайла);

Элементы.ПолеКартинки.Значение = Данные;

КонецЕсли;

КонецПроцедуры

Для больших файлов используйте ПотокЧтения с асинхронной загрузкой.