Сокращение строк в коде 1С: от обрезки до хранения в базе
Содержание
Чтобы обрезать строку до нужной длины в коде 1С, достаточно функции Лев(Строка, Длина) — она вернёт первые N символов. Уменьшить строку можно и другими способами: убрать лишние пробелы через СокрЛП, посчитать и урезать размер в байтах, сократить реквизит в базе или сжать длинный текст перед записью.
Под словом «уменьшить» прячутся пять разных задач: обрезать по количеству символов, уложиться в лимит байтов, почистить пробелы, сэкономить место в таблице и физически сжать содержимое. Инструменты у них разные, и путать их не стоит — именно на этом и возникает большинство ошибок.
Какой способ уменьшить строку выбрать
Сначала определите, что именно вам мешает: длина в символах, объём в байтах или место на диске. От этого зависит и функция, и риски.
Лев(Строка, Длина). Нужно многоточие — обрежьте до Длина - 1 и добавьте «…».ПолучитьДвоичныеДанныеИзСтроки(Текст, "UTF-8") и укорачивайте, пока не уложитесь в лимит.СокрЛП, затем цикл СтрЗаменить для двойных пробелов. Неразрывные пробелы заменяйте отдельно.ХранилищеЗначения с объектом СжатиеДанных, либо храните только хеш, если он нужен лишь для сравнения.Дальше — каждый способ с кодом и подводными камнями.
Обрезка строки до нужной длины
Три базовые функции делают почти всю работу с длиной:
- 📏
СтрДлина(Текст)— возвращает число символов в строке. - ✂️
Лев(Текст, Длина)— первыеДлинасимволов слева. - ✂️
Прав(Текст, Длина)— последниеДлинасимволов справа. - 🎯
Сред(Текст, Начало, Длина)— кусок с позицииНачало(позиции считаются с 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-8 | 2 | 20 байт |
| UTF-16 | 2 | 20 байт |
| Windows-1251 | 1 | 10 байт |
Когда нужно уложиться в лимит байтов (а не символов), обрезайте строку по одному символу и проверяйте размер:
Функция ОбрезатьПоБайтам(Текст, МаксБайт, Кодировка)
Пока ПолучитьДвоичныеДанныеИзСтроки(Текст, Кодировка).Размер() > МаксБайт Цикл
Текст = Лев(Текст, СтрДлина(Текст) - 1);
КонецЦикла;
Возврат Текст;
КонецФункции
Этот способ прост и надёжен для коротких строк: он гарантированно не разрежет многобайтовый символ пополам. Для длинных текстов цикл будет медленным — тогда сначала грубо обрежьте по символам, а потом доводите по байтам.
Windows-1251 стоит выбирать только для обмена со старыми системами, которые не понимают Unicode: на кириллице она вдвое компактнее UTF-8, но не хранит символы за пределами своей кодовой таблицы. У ПолучитьДвоичныеДанныеИзСтроки есть третий параметр — добавлять ли BOM (метку порядка байтов). Для UTF-8 в файлах обмена BOM чаще мешает принимающей стороне, поэтому по умолчанию его не добавляют.
Как сократить строку для хранения в базе
Если строка «тяжёлая» в таблице базы, причина обычно в типе реквизита. У строкового реквизита в конфигураторе два ключевых свойства:
- Длина — максимальное число символов. Значение
0означает неограниченную длину. - Допустимая длина — «Переменная» или «Фиксированная». Переменная хранит ровно столько, сколько реально записано; фиксированная резервирует всю объявленную длину.
Что это даёт на практике:
- ✅ Задайте реальную максимальную длину (например, 150 для наименования), а не оставляйте «неограниченную».
- ✅ Для обычных текстовых реквизитов выбирайте тип «Переменная» — он экономнее.
- ⛔ Неограниченные строки (длина 0) нельзя использовать в индексе, в упорядочивании, группировке и соединениях запроса. Это бьёт и по производительности, и по возможностям отбора.
Уменьшение длины реквизита с последующей реструктуризацией базы обрежет уже записанные значения до новой длины. Перед изменением структуры сделайте резервную копию и выполняйте реструктуризацию, когда пользователи не работают в базе.
Если переделывать метаданные нельзя, обрезайте значение программно перед записью — в обработчике ПередЗаписью объекта или групповой обработкой. Так вы не потеряете данные скрытно во время реструктуризации, а контролируете усечение сами.
Перед уменьшением длины полезно оценить, насколько это безопасно: пройдите по справочнику и посмотрите, у скольких элементов значение длиннее новой границы. Для перебора в коде подойдёт СтрДлина в сравнении с целевой длиной — так вы заранее увидите, какие записи обрежутся, и решите, что с ними делать.
Сжатие длинного текста: 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").Размер()));
Частые вопросы
Чем `Лев` отличается от `Сред`?
Лев всегда берёт символы с начала строки, Сред — с любой позиции. Если нужно начало — проще и быстрее Лев, Сред оставьте для вырезки из середины.Что вернёт `Лев`, если указать длину больше самой строки?
Лев не обязательно.Как обрезать строку, не разрезав слово посередине?
Лев, найдите последний пробел функцией СтрНайти(Кусок, " ", НаправлениеПоиска.СКонца) и отрежьте по нему. Так граница пройдёт по границе слова.Почему `СокрЛП` не убирает все пробелы?
СтрЗаменить до вызова СокрЛП.Как уменьшить размер большого текстового реквизита в базе?
ХранилищеЗначения со сжатием. Если нужен лимит по символам — задайте реальную длину и тип «Переменная» вместо неограниченной.