Сокращение строк в коде 1С: от обрезки до хранения в базе

· 9 мин

Содержание

Чтобы обрезать строку до нужной длины в коде 1С, достаточно функции Лев(Строка, Длина) — она вернёт первые N символов. Уменьшить строку можно и другими способами: убрать лишние пробелы через СокрЛП, посчитать и урезать размер в байтах, сократить реквизит в базе или сжать длинный текст перед записью.

Под словом «уменьшить» прячутся пять разных задач: обрезать по количеству символов, уложиться в лимит байтов, почистить пробелы, сэкономить место в таблице и физически сжать содержимое. Инструменты у них разные, и путать их не стоит — именно на этом и возникает большинство ошибок.

Какой способ уменьшить строку выбрать

Сначала определите, что именно вам мешает: длина в символах, объём в байтах или место на диске. От этого зависит и функция, и риски.

Что нужно сделать со строкой?

Дальше — каждый способ с кодом и подводными камнями.

Обрезка строки до нужной длины

Три базовые функции делают почти всю работу с длиной:

  • 📏 СтрДлина(Текст) — возвращает число символов в строке.
  • ✂️ Лев(Текст, Длина) — первые Длина символов слева.
  • ✂️ Прав(Текст, Длина) — последние Длина символов справа.
  • 🎯 Сред(Текст, Начало, Длина) — кусок с позиции Начало (позиции считаются с 1).

Простейший случай — отрезать строку до 50 символов:

Результат = Лев(Исходная, 50);

Если строка короче 50 символов, Лев вернёт её целиком, без ошибки. Поэтому дополнительную проверку длины делать не нужно — функция сама не выйдет за границы.

Часто обрезку хотят делать «красиво», с многоточием на конце. Тогда резервируем один символ под знак «…»:

Функция ОбрезатьСТочками(Текст, МаксДлина)
    Если СтрДлина(Текст) <= МаксДлина Тогда
        Возврат Текст;
    КонецЕсли;
    Возврат Лев(Текст, МаксДлина - 1) + "…";
КонецФункции

На входе Текст = "Поступление товаров и услуг № 000000123" и МаксДлина = 20 функция вернёт строку ровно из 20 символов: 19 символов текста плюс многоточие.

Многоточие «…» — это один символ (код 8230), а не три точки подряд. Если поставить три точки ..., итоговая длина окажется на два символа больше задуманной.

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

Кусок = Сред("2026-10-ХХ", 7, 4); // вернёт "0-ХХ"

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

Иногда отрезать нужно не начало, а хвост — скажем, последние четыре цифры счёта или карты. Тогда берите Прав:

Хвост = Прав(НомерСчёта, 4); // последние 4 символа

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

Удаление лишних пробелов и невидимых символов

Строка бывает длинной не из-за содержания, а из-за мусора по краям и в середине. Классика — выгрузка из Excel или веб-формы, где к значению прилипают пробелы.

Края чистит одна функция:

  • СокрЛ(Текст) — убирает пробелы слева.
  • СокрП(Текст) — убирает пробелы справа.
  • СокрЛП(Текст) — убирает пробелы с обеих сторон.

Внутренние двойные пробелы СокрЛП не трогает. Чтобы схлопнуть их в один, прогоните цикл замены:

Пока СтрНайти(Текст, "  ") > 0 Цикл
    Текст = СтрЗаменить(Текст, "  ", " ");
КонецЦикла;

Цикл конечный: каждая итерация убирает хотя бы один лишний пробел, пока двойных не останется.

СокрЛП чистит обычный пробел (код 32), но не убирает неразрывный пробел (код 160) и табуляцию (код 9). Из HTML и почты они приходят постоянно, а визуально неотличимы от обычных.

Поэтому перед обрезкой нормализуйте невидимые символы:

Текст = СтрЗаменить(Текст, Символ(160), " "); // неразрывный пробел
Текст = СтрЗаменить(Текст, Символ(9), " ");   // табуляция
Текст = СтрЗаменить(Текст, Символ(10), " ");  // перевод строки
Текст = СтрЗаменить(Текст, Символ(13), " ");  // возврат каретки
Текст = СокрЛП(Текст);

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

Размер строки в байтах и кодировки

Длина в символах и размер в байтах — не одно и то же. СтрДлина считает символы, а внешние системы, файлы и ограничения протоколов часто меряют байтами.

В 1С строки внутри хранятся в Unicode. Фактический размер зависит от кодировки, в которую вы их переводите. Посчитать байты можно так:

Данные = ПолучитьДвоичныеДанныеИзСтроки(Текст, "UTF-8");
Сообщить("Размер: " + Данные.Размер() + " байт");

В UTF-8 один символ кириллицы занимает 2 байта, латиница и цифры — по 1 байту. Значит слово из 10 русских букв — это 10 символов, но 20 байт. Если вас ограничивают по байтам, считать надо именно так.

Сравнение кодировок для слова из 10 кириллических символов:

КодировкаБайт на символ (кириллица)Итого на 10 символов
UTF-8220 байт
UTF-16220 байт
Windows-1251110 байт

Когда нужно уложиться в лимит байтов (а не символов), обрезайте строку по одному символу и проверяйте размер:

Функция ОбрезатьПоБайтам(Текст, МаксБайт, Кодировка)
    Пока ПолучитьДвоичныеДанныеИзСтроки(Текст, Кодировка).Размер() > МаксБайт Цикл
        Текст = Лев(Текст, СтрДлина(Текст) - 1);
    КонецЦикла;
    Возврат Текст;
КонецФункции

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

Windows-1251 стоит выбирать только для обмена со старыми системами, которые не понимают Unicode: на кириллице она вдвое компактнее UTF-8, но не хранит символы за пределами своей кодовой таблицы. У ПолучитьДвоичныеДанныеИзСтроки есть третий параметр — добавлять ли BOM (метку порядка байтов). Для UTF-8 в файлах обмена BOM чаще мешает принимающей стороне, поэтому по умолчанию его не добавляют.

Как сократить строку для хранения в базе

Если строка «тяжёлая» в таблице базы, причина обычно в типе реквизита. У строкового реквизита в конфигураторе два ключевых свойства:

  1. Длина — максимальное число символов. Значение 0 означает неограниченную длину.
  2. Допустимая длина — «Переменная» или «Фиксированная». Переменная хранит ровно столько, сколько реально записано; фиксированная резервирует всю объявленную длину.

Что это даёт на практике:

  • ✅ Задайте реальную максимальную длину (например, 150 для наименования), а не оставляйте «неограниченную».
  • ✅ Для обычных текстовых реквизитов выбирайте тип «Переменная» — он экономнее.
  • ⛔ Неограниченные строки (длина 0) нельзя использовать в индексе, в упорядочивании, группировке и соединениях запроса. Это бьёт и по производительности, и по возможностям отбора.

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

Если переделывать метаданные нельзя, обрезайте значение программно перед записью — в обработчике ПередЗаписью объекта или групповой обработкой. Так вы не потеряете данные скрытно во время реструктуризации, а контролируете усечение сами.

Перед уменьшением длины полезно оценить, насколько это безопасно: пройдите по справочнику и посмотрите, у скольких элементов значение длиннее новой границы. Для перебора в коде подойдёт СтрДлина в сравнении с целевой длиной — так вы заранее увидите, какие записи обрежутся, и решите, что с ними делать.

Перед уменьшением длины реквизита
Выполнено: 0 / 5

Сжатие длинного текста: zip, хеш и Base64

Когда в реквизите лежат мегабайты текста (логи, выгрузки, XML), экономит место именно сжатие, а не обрезка. В 1С для этого удобен тип ХранилищеЗначения с указанием степени сжатия от 0 до 9:

Хранилище = Новый ХранилищеЗначения(БольшойТекст, Новый СжатиеДанных(9));
// при чтении
БольшойТекст = Хранилище.Получить();

Степень 9 даёт максимальное сжатие, 0 — без сжатия. Для текста выигрыш часто кратный, потому что в нём много повторов: XML-выгрузка на несколько мегабайт после упаковки нередко ужимается в разы. Расплата — процессорное время на сжатие и распаковку, поэтому для часто читаемых значений берите степень пониже, а максимальную оставьте для архивных данных, которые читают редко.

Разложить содержимое обратно в текст помогает метод Получить() того же хранилища — распаковка происходит прозрачно, отдельного вызова для декомпрессии не нужно.

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

Хеш = Новый ХешированиеДанных(ХешФункция.MD5);
Хеш.Добавить(ПолучитьДвоичныеДанныеИзСтроки(БольшойТекст, "UTF-8"));
Отпечаток = Base64Строка(Хеш.ХешСумма); // короткая строка для хранения

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

Base64 — это не сжатие. Он переводит двоичные данные в текст и, наоборот, увеличивает объём примерно на треть. Base64 нужен для передачи и сериализации, а не для экономии места.

Частые ошибки при уменьшении строк

Эти грабли встречаются чаще самих задач — большинство проблем с длиной именно отсюда.

  • ❌ Склейка строки с числом. Выражение "Остаток: " + Количество вызовет ошибку преобразования, если Количество — число. Приводите явно: "Остаток: " + Формат(Количество, "ЧГ=0") или СтрШаблон("Остаток: %1", Количество).
  • ❌ Путают символы и байты. Реквизит на 100 символов и лимит в 100 байт — разные ограничения. Для кириллицы в UTF-8 в 100 байт влезет только около 50 символов.
  • ❌ Считают позицию с нуля. В Сред(Текст, Начало, Длина) первый символ имеет позицию 1. Нулевая или отрицательная позиция даст не тот кусок.
  • ❌ Забывают про неразрывный пробел. СокрЛП оставил пробел? Значит это символ 160 или табуляция — замените их отдельно до обрезки.
  • ❌ СтрЗаменить не сработал. Функция чувствительна к регистру: СтрЗаменить(Текст, "ООО", "") не тронет «ооо». При необходимости приводите обе части к одному регистру через ВРег или НРег.

Для отладки длины удобно выводить и символы, и байты одновременно — тогда сразу видно, какой лимит вы нарушаете:

Сообщить(СтрШаблон("Символов: %1, байт (UTF-8): %2",
    СтрДлина(Текст),
    ПолучитьДвоичныеДанныеИзСтроки(Текст, "UTF-8").Размер()));

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

Чем `Лев` отличается от `Сред`?
Лев всегда берёт символы с начала строки, Сред — с любой позиции. Если нужно начало — проще и быстрее Лев, Сред оставьте для вырезки из середины.
Что вернёт `Лев`, если указать длину больше самой строки?
Всю строку целиком, без ошибки и без дополнения пробелами. Поэтому проверять длину перед вызовом Лев не обязательно.
Как обрезать строку, не разрезав слово посередине?
Обрежьте до нужной длины через Лев, найдите последний пробел функцией СтрНайти(Кусок, " ", НаправлениеПоиска.СКонца) и отрежьте по нему. Так граница пройдёт по границе слова.
Почему `СокрЛП` не убирает все пробелы?
Он убирает только обычные пробелы (код 32) по краям. Неразрывные пробелы (код 160), табуляцию (код 9) и переносы строк замените через СтрЗаменить до вызова СокрЛП.
Как уменьшить размер большого текстового реквизита в базе?
Если текст нужен целиком — храните его в реквизите типа ХранилищеЗначения со сжатием. Если нужен лимит по символам — задайте реальную длину и тип «Переменная» вместо неограниченной.