Цифры внутри текста: перебор, СтрРазделить, регулярные выражения и БСП
Содержание
Найти цифру в строке 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.RegExp | COM-объект Windows | Быстро после создания объекта | Только Windows, нет в веб-клиенте |
ПОДОБНО в запросе | СУБД | Лучший вариант для фильтра по базе | Только отбор, без извлечения |
Общее правило: чем меньше итераций выполняет встроенный язык, тем быстрее. Каждая строка кода в цикле — это отдельная интерпретируемая операция, а СтрНайти и СтрРазделить проходят строку в машинном коде платформы.
Цифры для своего случая проще получить замером на реальных данных:
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
Для Счетчик = 1 По 10000 Цикл
Результат = ГруппыЦифрБыстро(ТестоваяСтрока);
КонецЦикла;
Сообщить("Мс: " + Формат(ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало, "ЧГ=0"));
Для сравнения запустите тот же цикл с ГруппыЦифр(). Ещё точнее покажет замер производительности в режиме отладки конфигуратора: там видно время каждой строки.
Частые вопросы
Как удалить все цифры из строки 1С?
СтрЗаменить(Текст, Строка(Цифра), "") в цикле от 0 до 9. Это быстрее посимвольного перебора и работает на любой платформе 8.3.Почему Число("12,5") вызывает ошибку?
Число() ожидает точку как разделитель дробной части. Замените запятую на точку и уберите пробелы до преобразования, а сам вызов оберните в Попытка … Исключение.Можно ли найти цифру в строке в системе компоновки данных?
%[0-9]%. Извлечь цифры в вычисляемом поле можно, только вызвав экспортную функцию общего модуля.Как посчитать количество цифр в строке?
СтрДлина(Текст) - СтрДлина(ТекстБезЦифр). Получится число цифровых символов без цикла.Сработает ли код в веб-клиенте?
СтрНайти, СтрРазделить и функции БСП — да. COM-объект VBScript.RegExp — нет: перенесите такой код в серверную процедуру на сервере под Windows.