Двоичные данные в 1С:Предприятие — это тема, которая пугает многих разработчиков из-за кажущейся сложности. На практике же работа с бинарными потоками, файлами в формате .bin, изображениями или архивами требуется чаще, чем кажется: от загрузки прайс-листов поставщиков до интеграции с оборудованием. Главная проблема — отсутствие встроенных "человекопонятных" инструментов для визуализации таких данных. Но как только вы поймёте базовые принципы, двоичные операции станут таким же обыденным делом, как работа с реквизитами справочников.
В этой статье мы разберём все ключевые сценарии: от простого чтения файла до сложной обработки потоков с использованием ДвоичныеДанные, ПотокВПамяти и ЗаписьДанных. Особое внимание уделим типовым ошибкам, которые приводят к "битым" файлам или падению производительности. Если вы когда-нибудь сталкивались с сообщением "Неверный формат потока" или "Ошибка чтения двоичных данных", после прочтения вы будете знать, как их избежать.
Для начинающих программистов 1С работа с двоичными данными часто ассоциируется с низкоуровневым программированием — мол, это "не для нас". На самом деле, в 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. Обработка изображений и мультимедиа
Изображения, аудио и видео файлы — это тоже двоичные данные, но с особенностями. В 1С для работы с ними есть специализированные объекты: Картинка, ФайлКартинки и методы для манипуляции пикселями. Однако часто требуется низкоуровневая обработка, например, для:
- 🖼️ Извлечения EXIF-меток из фотографий.
- 🔍 Проверки целостности файлов по сигнатуре.
- 📦 Упаковки нескольких изображений в один контейнер.
Пример чтения сигнатуры JPEG-файла (первые 2 байта должны быть 0xFFD8):
Функция ЭтоJPEG(ПутьКФайлу)
Данные = Новый ДвоичныеДанные(ПутьКФайлу);
Если Данные.Размер() < 2 Тогда
Возврат Ложь;
КонецЕсли;
Поток = Новый ПотокЧтения(Данные);
Сигнатура = Поток.ПрочитатьЧисло(2);
Возврат Сигнатура = 0xFFD8;
КонецФункции
Для извлечения метаданных из изображений можно использовать внешние компоненты (например, AddIn на C#), но простейшие операции доступны и на чистой 1С. Например, так можно получить размеры PNG-файла (сигнатура 0x89504E47, ширина и высота хранятся на смещении 16 байт):
Функция ПолучитьРазмерыPNG(ДвоичныеДанныеФайла)
Если ДвоичныеДанныеФайла.Размер() < 24 Тогда
Возврат Неопределено;
КонецЕсли;
Поток = Новый ПотокЧтения(ДвоичныеДанныеФайла);
// Пропускаем заголовок (8 байт) и chunk IHDR (12 байт)
Поток.Пропустить(20);
Ширина = Поток.ПрочитатьЧисло(4);
Высота = Поток.ПрочитатьЧисло(4);
Возврат Новый Структура("Ширина, Высота", Ширина, Высота);
КонецФункции
Проверьте сигнатуру файла (первые 2-8 байт)|Убедитесь, что размер файла соответствует заявленному|Проверьте CRC/контрольные суммы (если поддерживаются форматом)|Используйте Попытка..Исключение для обработки ошибок-->
5. Сериализация и десериализация данных
Объекты ЗаписьДанных и ЧтениеДанных предназначены для преобразования структурированных данных в двоичный формат и обратно. Это аналог BinaryFormatter в .NET или pickle в Python, но с ограничениями 1С. Главное правило: формат данных должен быть согласован между записью и чтением.
Пример сериализации массива структур:
ДанныеДляЗаписи = Новый Массив;
ДанныеДляЗаписи.Добавить(Новый Структура("Имя, Возраст", "Иван", 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); // Исключение!
Ещё одна частая проблема — неверная интерпретация строк. По умолчанию 1С использует кодировку UTF-16 (2 байта на символ), но многие внешние системы ожидают UTF-8 или Windows-1251. Всегда явно указывайте кодировку:
Поток.ЗаписатьСтроку("Привет", КодировкаТекста.UTF8);
Для отладки двоичных данных используйте функцию Base64Строка(), чтобы преобразовать байты в читаемый формат: Сообщить(ДвоичныеДанные.Base64Строка()).
7. Продвинутые сценарии: шифрование, сжатие, работа с оборудованием
Для сложных задач стандартных средств 1С может не хватить. В таких случаях используют:
- 🔐 Шифрование: внешние компоненты (например, 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.
Примеры оптимизации:
- Используйте
ПотокВПамятидля промежуточных операций вместо создания множества объектовДвоичныеДанные. - При работе с большими файлами читайте/пишите данные блоками по 4096–8192 байт:
РазмерБлока = 8192;
ПотокЧтения = Новый ПотокЧтения(ИсходныйФайл);
ПотокЗаписи = Новый ПотокЗаписи(ЦелевойФайл);
Пока ПотокЧтения.Доступно() > 0Loop
Данные = ПотокЧтения.Прочитать(Min(ПотокЧтения.Доступно(), РазмерБлока));
ПотокЗаписи.Записать(Данные);
КонецЦикла;
- Избегайте
Base64для хранения больших двоичных данных в базе — используйтеХранилищеЗначения.
Для длительных операций (например, обработки видеофайлов) используйте фоновые задания:
ФоновоеЗадание = ФоновыеЗадания.Создать("ОбработкаФайла",
Новый Структура("ПутьКФайлу", "C:\temp\large.bin"),
Новый ОписаниеОповещения("ЗавершениеОбработки", ЭтотОбъект));
ФоновоеЗадание.ВыполнитьАсинхронно();
Для файлов размером более 100 МБ используйте потоковую обработку (чтение/запись блоками) вместо загрузки всего файла в память.
⚠️ Внимание: При работе с большими двоичными данными в веб-клиенте 1С учитывайте ограничения на размер передаваемых данных (по умолчанию — 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): по умолчанию 1С использует Little-Endian.
Как работать с двоичными данными в управляемых формах?
Для отображения двоичных данных (например, изображений) используйте реквизит типа ХранилищеЗначения или ДвоичныеДанные с привязкой к элементу формы ПолеДвоичныхДанных:
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Если Диалог.Выбрать() Тогда
Данные = Новый ДвоичныеДанные(Диалог.ПолноеИмяФайла);
Элементы.ПолеКартинки.Значение = Данные;
КонецЕсли;
КонецПроцедуры
Для больших файлов используйте ПотокЧтения с асинхронной загрузкой.