Цифры внутри текста: перебор, СтрРазделить, регулярные выражения и БСП

· 10 мин

Содержание

Найти цифру в строке 1С проще всего перебором: идёте по символам через Сред() и проверяете КодСимвола() — у цифр от 0 до 9 коды с 48 по 57. Если нужна только позиция первой цифры, быстрее десять вызовов СтрНайти(), по одному на каждую цифру. На платформе 8.3.23 и новее есть встроенные функции регулярных выражений.

Типичная боль: из «Счёт № 145 от 03.09» нужно вытащить номер, из артикула «AB-0072/К» — числовую часть, а из наименования понять, есть ли в нём цифры вообще. Для каждого случая есть своя функция, и они заметно различаются по скорости.

С чего начать: что именно вы ищете в строке

Прежде чем писать код, сформулируйте результат. От него зависит метод.

ЗадачаЧто вернутьПодходящий приём
Есть ли в строке хоть одна цифраБулевоСтрНайти по 10 цифрам, ПОДОБНО в запросе
Где первая цифраЧисло (позиция)СтрНайти по 10 цифрам
Все цифры подряд, без остальногоСтрокаЦикл или СтрРазделить
Отдельные числа («145», «03», «09»)Массив строкСтрРазделить, регулярное выражение
Строка целиком из цифрБулевоФункция БСП ТолькоЦифрыВСтроке
Число с дробной частью и пробеламиЧислоОчистка строки + Число() в Попытке

Два вопроса, которые стоит задать себе сразу:

  • 🔢 Нужны ведущие нули? Артикул «0072» и число 72 — разные вещи. ИНН, КПП, номер счёта и штрихкод держите строкой.
  • 🧩 Считать ли минус и запятую частью числа? «-15» и «12,5» обычная проверка на цифры разорвёт на части.

Перебор символов в цикле — универсальный способ

Цикл работает на любой версии платформы, на клиенте и на сервере, в любой конфигурации. Его минус — скорость на длинных текстах, но для наименований и номеров документов это не заметно.

Проверка одного символа

Два равноценных варианта:

// Вариант 1: по коду символа
Код = КодСимвола(Текст, Позиция);
ЭтоЦифра = (Код >= 48 И Код <= 57);

// Вариант 2: по вхождению в строку-образец
ЭтоЦифра = СтрНайти("0123456789", Сред(Текст, Позиция, 1)) > 0;

КодСимвола(Текст, Позиция) принимает позицию вторым параметром, поэтому лишний вызов Сред() не нужен. Обратная функция — Символ(Код): Символ(53) вернёт «5».

Оставить в строке только цифры

Функция ТолькоЦифры(Знач Текст) Экспорт

    Части = Новый Массив;
    Для Позиция = 1 По СтрДлина(Текст) Цикл
        Код = КодСимвола(Текст, Позиция);
        Если Код >= 48 И Код <= 57 Тогда
            Части.Добавить(Символ(Код));
        КонецЕсли;
    КонецЦикла;

    Возврат СтрСоединить(Части, "");

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

Для «Тел.: +7 (912) 345-67-89» функция вернёт «79123456789». Строку собираем через массив и СтрСоединить(), а не сложением в цикле: на длинных текстах конкатенация заметно тормозит.

Разбить строку на отдельные числа

Если важны группы цифр, копите текущую группу и сбрасывайте её на первом нецифровом символе:

Функция ГруппыЦифр(Знач Текст) Экспорт

    Результат = Новый Массив;
    Текущая = "";

    Для Позиция = 1 По СтрДлина(Текст) Цикл
        ТекСимвол = Сред(Текст, Позиция, 1);
        Если СтрНайти("0123456789", ТекСимвол) > 0 Тогда
            Текущая = Текущая + ТекСимвол;
        ИначеЕсли Текущая <> "" Тогда
            Результат.Добавить(Текущая);
            Текущая = "";
        КонецЕсли;
    КонецЦикла;

    Если Текущая <> "" Тогда
        Результат.Добавить(Текущая);
    КонецЕсли;

    Возврат Результат;

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

Для «Счёт № 145 от 03.09» получится массив из трёх строк: «145», «03», «09». Последняя проверка после цикла обязательна — иначе потеряется число в самом конце строки.

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

Трюк без цикла: СтрРазделить по «нецифрам»

У СтрРазделить() есть особенность, которую многие упускают: каждый символ разделителя работает как отдельный разделитель. Передайте ей строку из всех нецифровых символов текста — и она сама нарежет текст на группы цифр.

Набор «нецифр» получаем, удалив из текста все цифры десятью вызовами СтрЗаменить():

Функция ГруппыЦифрБыстро(Знач Текст) Экспорт

    НеЦифры = Текст;
    Для Цифра = 0 По 9 Цикл
        НеЦифры = СтрЗаменить(НеЦифры, Строка(Цифра), "");
    КонецЦикла;

    Если НеЦифры = "" Тогда
        // В строке только цифры (или она пустая)
        Результат = Новый Массив;
        Если Текст <> "" Тогда
            Результат.Добавить(Текст);
        КонецЕсли;
        Возврат Результат;
    КонецЕсли;

    Возврат СтрРазделить(Текст, НеЦифры, Ложь);

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

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

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

СтрРазделить() и СтрСоединить() появились в платформе 8.3.6. В старых конфигурациях с режимом совместимости ниже этой версии их не будет — используйте цикл.

Регулярные выражения: встроенные функции и COM-объект

Регулярное выражение \d+ описывает «одна или больше цифр подряд» и решает задачу одной строкой. В 1С есть два пути.

Встроенные функции платформы 8.3.23+

В платформе 8.3.23 появилось семейство функций с «РегулярномуВыражению» в имени: поиск первого совпадения, поиск всех совпадений, замена, разделение. Пример — все числа из строки:

Совпадения = СтрНайтиВсеПоРегулярномуВыражению(Текст, "\d+");
Для Каждого Совпадение Из Совпадения Цикл
    Сообщить(Совпадение.Значение);
КонецЦикла;

Состав параметров и свойств результата сверьте в синтакс-помощнике своей версии платформы: если режим совместимости конфигурации ниже 8.3.23, функции будут недоступны даже на новой платформе.

Полезные шаблоны:

  • \d — одна цифра;
  • \d+ — целое число;
  • -?\d+([.,]\d+)? — число с минусом и дробной частью через точку или запятую;
  • \d{10}|\d{12} — кандидат в ИНН (10 или 12 цифр подряд).

VBScript.RegExp через COM

На старых платформах под Windows подключают системный объект:

RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.Pattern = "\d+";
RegExp.Global = Истина;

Совпадения = RegExp.Execute(Текст);
Для Индекс = 0 По Совпадения.Count - 1 Цикл
    Сообщить(Совпадения.Item(Индекс).Value);
КонецЦикла;

Ограничения этого пути:

  • 🪟 работает только там, где код выполняется под Windows: на сервере 1С под Linux и в веб-клиенте не заработает;
  • 🐢 создание COM-объекта — дорогая операция, создавайте его один раз до цикла по документам;
  • 🧯 Microsoft выводит VBScript из стандартной поставки Windows, поэтому на новых серверах компонент может отсутствовать.

Функции БСП и запрос: что уже написано за вас

Почти во всех типовых конфигурациях (БП 3.0, ЗУП 3.1, УТ 11) есть Библиотека стандартных подсистем. Строковые утилиты лежат в общем модуле СтроковыеФункцииКлиентСервер.

Функция БСПЧто делает
ТолькоЦифрыВСтроке(Значение)Истина, если строка состоит только из цифр
ТолькоЛатиницаВСтроке(Значение)Истина, если в строке только латиница
РазложитьСтрокуВМассивПодстрок(Строка, Разделитель)Разбивает строку по разделителю

ТолькоЦифрыВСтроке удобна для проверки ИНН, КПП, БИК и кодов: одна строка вместо собственного цикла. У неё есть необязательные параметры, которые меняют обработку пробелов и расширенных символов, — их смысл посмотрите в комментарии к функции в модуле, он различается между редакциями БСП.

Чтобы найти нужную функцию, откройте модуль СтроковыеФункцииКлиентСервер в конфигураторе и вызовите поиск по слову «Цифр». Так быстрее, чем гадать по документации, и вы увидите именно свою версию библиотеки.

Цифры в данных базы: ПОДОБНО в запросе

Если нужно найти не цифру в одной строке, а все элементы справочника с цифрами в наименовании, не перебирайте их в коде. Язык запросов понимает диапазон символов в шаблоне ПОДОБНО:

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Наименование ПОДОБНО "%[0-9]%"

% означает любую последовательность символов, [0-9] — один символ из диапазона. Для обратной выборки («без цифр») используйте НЕ ... ПОДОБНО "%[0-9]%". Вытащить сами цифры запросом нельзя — только отфильтровать строки, а дальше обработать их кодом.

Позиция первой и последней цифры

Позиция нужна, когда строку режут: «всё до первой цифры — префикс, остальное — номер». Самый быстрый вариант — минимум из десяти вызовов СтрНайти():

Функция ПозицияПервойЦифры(Знач Текст) Экспорт

    Минимум = 0;
    Для Цифра = 0 По 9 Цикл
        Поз = СтрНайти(Текст, Строка(Цифра));
        Если Поз > 0 И (Минимум = 0 Или Поз < Минимум) Тогда
            Минимум = Поз;
        КонецЕсли;
    КонецЦикла;

    Возврат Минимум; // 0 — цифр нет

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

Для последней цифры ищите с конца и берите максимум:

Поз = СтрНайти(Текст, Строка(Цифра), НаправлениеПоиска.СКонца);

Пример: для «ПТУ-00045» первая цифра стоит на позиции 5. Префикс получаем как Лев(Текст, 4), номер — Сред(Текст, 5).

На платформах до 8.3.6 вместо СтрНайти() используйте Найти(Строка, Подстрока): она ищет только первое вхождение слева, поэтому последнюю цифру там находят циклом с конца строки.

Граничные случаи, на которых ломается код

Функция, которая работает на «Счёт № 145», часто падает на реальных данных. Проверьте её на этих входах.

  • 📭 Пустая строка и Неопределено. СтрДлина("") = 0, цикл не выполнится, это нормально. А вот Неопределено из незаполненного реквизита приведите к строке заранее: Текст = Строка(Значение).
  • 🧹 Пробелы внутри числа. «1 250 000» после печатной формы часто содержит неразрывный пробел. Удаляйте оба: СтрЗаменить(Текст, " ", "") и СтрЗаменить(Текст, Символы.НПП, "").
  • ➖ Знак минуса и дробная часть. Цикл по цифрам превратит «-12,5» в «125». Если нужны числа со знаком — используйте шаблон регулярного выражения из раздела выше.
  • 0️⃣ Ведущие нули. Число("0072") вернёт 72. Для кодов и артикулов храните результат строкой.
  • 🔣 «Похожие» символы. Надстрочное «²» в «м²», цифры из других алфавитов и буква «О» вместо нуля проверку КодСимвола 48–57 не пройдут. Обычно это правильно, но в выгрузках из сканов встречается «О» в номерах — чистите такие данные отдельно.

Безопасное превращение строки в число

Число() вызывает исключение на некорректной строке, поэтому оборачивайте преобразование:

Функция ЧислоИзСтроки(Знач Текст, ЗначениеПоУмолчанию = 0) Экспорт

    Текст = СтрЗаменить(Текст, " ", "");
    Текст = СтрЗаменить(Текст, Символы.НПП, "");
    Текст = СтрЗаменить(Текст, ",", ".");

    Попытка
        Возврат Число(Текст);
    Исключение
        Возврат ЗначениеПоУмолчанию;
    КонецПопытки;

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

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

Если код меняет реквизиты в рабочей базе (например, переносит номер из наименования в отдельное поле), сначала прогоните его на копии и выведите результат через Сообщить(), без записи объектов.

Какой метод быстрее и когда это важно

На строке в 30 символов разница между методами — микросекунды. Она становится заметной, когда вы разбираете десятки тысяч строк или текстовые файлы целиком.

МетодГде выполняется работаСкорость на длинных строкахОграничения
Цикл с КодСимволаВстроенный язык, по символуСамый медленныйНет
10 × СтрНайти (позиция)Внутри платформыБыстроТолько позиция или факт наличия
СтрЗаменить + СтрРазделитьВнутри платформыБыстро на строках до сотен символовПлатформа 8.3.6+
Встроенные регулярные выраженияВнутри платформыБыстро, удобно для сложных шаблоновПлатформа и совместимость 8.3.23+
VBScript.RegExpCOM-объект WindowsБыстро после создания объектаТолько Windows, нет в веб-клиенте
ПОДОБНО в запросеСУБДЛучший вариант для фильтра по базеТолько отбор, без извлечения

Общее правило: чем меньше итераций выполняет встроенный язык, тем быстрее. Каждая строка кода в цикле — это отдельная интерпретируемая операция, а СтрНайти и СтрРазделить проходят строку в машинном коде платформы.

Цифры для своего случая проще получить замером на реальных данных:

Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
Для Счетчик = 1 По 10000 Цикл
    Результат = ГруппыЦифрБыстро(ТестоваяСтрока);
КонецЦикла;
Сообщить("Мс: " + Формат(ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало, "ЧГ=0"));

Для сравнения запустите тот же цикл с ГруппыЦифр(). Ещё точнее покажет замер производительности в режиме отладки конфигуратора: там видно время каждой строки.

Какой способ выбрать?

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

Как удалить все цифры из строки 1С?
Десять раз вызовите СтрЗаменить(Текст, Строка(Цифра), "") в цикле от 0 до 9. Это быстрее посимвольного перебора и работает на любой платформе 8.3.
Почему Число("12,5") вызывает ошибку?
Число() ожидает точку как разделитель дробной части. Замените запятую на точку и уберите пробелы до преобразования, а сам вызов оберните в Попытка … Исключение.
Можно ли найти цифру в строке в системе компоновки данных?
В отборе СКД доступно условие «Подобно» с тем же синтаксисом шаблона, что и в запросе: %[0-9]%. Извлечь цифры в вычисляемом поле можно, только вызвав экспортную функцию общего модуля.
Как посчитать количество цифр в строке?
Сравните длину до и после удаления цифр: СтрДлина(Текст) - СтрДлина(ТекстБезЦифр). Получится число цифровых символов без цикла.
Сработает ли код в веб-клиенте?
Цикл, СтрНайти, СтрРазделить и функции БСП — да. COM-объект VBScript.RegExp — нет: перенесите такой код в серверную процедуру на сервере под Windows.