Извлечение и поиск чисел в строковых данных 1С 8.3

· 9 мин

Содержание

Чтобы найти число в строке 1С, есть три основных приёма: штатная функция СтрНайти ищет позицию уже известного числа, перебор символов через КодСимвола вытаскивает любые цифры из произвольного текста, а объект РегулярноеВыражение (платформа 8.3.23 и новее) ловит числовые группы по шаблону \d+. Выбор зависит от задачи: найти известный номер, собрать все цифры или проверить, что строка целиком является числом.

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

Чем отличаются задачи «найти число»

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

  • 🔍 Найти позицию известного числа — вы знаете, что ищете 1250, и хотите узнать, на каком месте оно стоит. Подходит СтрНайти.
  • 🧮 Вытащить все цифры или группы цифр — из строки Заказ №45 от 03.05 нужно получить 45 или 4503. Нужен перебор символов или регулярное выражение.
  • ✅ Проверить, что строка целиком — число — поле ввода содержит "1250,50", и его надо превратить в число типа Число. Работает преобразование внутри Попытка.
  • 🗄️ Отобрать записи, где в поле есть цифры — это задача запроса, а не кода.

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

Что именно нужно сделать с числом в строке?

Поиск позиции числа штатными функциями

Если число известно заранее — номер счёта, код, артикул, — его позицию возвращает функция СтрНайти. Это современная замена устаревшей Найти; она умеет искать с начала и с конца, а также находить N-е вхождение.

Позиция = СтрНайти("Счёт № 1250 от 03.05", "1250");
// Позиция = 8 — число "1250" начинается с восьмого символа

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

Если СтрНайти(Текст, "1250") > 0 Тогда
    // число в строке есть
КонецЕсли;

Когда известна позиция, сам фрагмент вырезают функцией Сред(Строка, Начало, Длина). Для края строки удобны Лев и Прав:

Текст    = "Накладная 45";
Позиция  = СтрНайти(Текст, "45");          // 11
Фрагмент = Сред(Текст, Позиция, 2);        // "45"

Чтобы найти ПОСЛЕДНЕЕ вхождение числа, передайте третьим параметром НаправлениеПоиска.СКонца. Это полезно, когда в строке несколько похожих номеров, а нужен последний.

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

Перебор символов: универсальный способ вытащить цифры

Когда конкретное число неизвестно, строку проходят посимвольно и отбирают только цифры. Способ работает в любой версии платформы и не зависит от релиза. Цифра определяется по коду символа: у знаков от 0 до 9 коды с 48 по 57.

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

Вызов ВытащитьЦифры("Заказ 1250/А, 3 шт") вернёт строку "12503" — все цифры подряд. Учтите: такой результат склеивает разные числа в одно, поэтому для разбора номеров он годится, а для суммы и количества — нет.

Чаще нужна первая самостоятельная группа цифр — например, номер из строки Счёт № 1250 от 03.05. Тогда накапливаем цифры, а на первом же нечисловом символе после них выходим из цикла:

Функция ПерваяГруппаЦифр(Исходная)
    Результат = "";
    Для Поз = 1 По СтрДлина(Исходная) Цикл
        Код = КодСимвола(Исходная, Поз);
        Если Код >= 48 И Код <= 57 Тогда
            Результат = Результат + Сред(Исходная, Поз, 1);
        ИначеЕсли НЕ ПустаяСтрока(Результат) Тогда
            Прервать;
        КонецЕсли;
    КонецЦикла;
    Возврат Результат;
КонецФункции

ПерваяГруппаЦифр("Счёт № 1250 от 03.05") вернёт "1250": как только после цифр встречается пробел, цикл прерывается, и дата 03.05 в результат не попадает.

Проверку по коду символа (КодСимвола) предпочитают вызову Сред в условии: она не создаёт промежуточную строку из одного символа и на длинном тексте работает заметно быстрее.

Регулярные выражения для числовых шаблонов

На платформе 8.3.23 и новее появился встроенный объект РегулярноеВыражение — он находит числа по шаблону без ручного перебора. Шаблон \d+ означает «одна или больше цифр подряд», то есть каждую числовую группу в тексте.

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

Метод НайтиСовпадения возвращает массив найденных совпадений; у каждого в свойстве Значение лежит найденная подстрока. Несколько полезных шаблонов:

ШаблонЧто находит
\d+целые числа (группы цифр)
\d+[.,]\d+дробные с точкой или запятой
-?\d+целые со знаком минус
\d{4}ровно четыре цифры (год, часть кода)

Если платформа старше 8.3.23, объект РегулярноеВыражение недоступен — используйте перебор символов из раздела выше. Точный набор свойств объекта (группы, индекс, длина совпадения) смотрите во встроенном синтакс-помощнике вашего релиза.

Регулярные выражения удобны, когда чисел в строке много или они разного формата. Для одного простого случая перебор символов остаётся и короче, и совместимее со старыми базами.

Преобразование найденного в число и обработка ошибок

Найденная цифровая строка остаётся строкой. Чтобы считать, её переводят в тип Число функцией Число(). Но если во фрагмент попал нечисловой символ, вызов Число() приводит к ошибке и прерывает выполнение. Поэтому преобразование оборачивают в Попытка … Исключение:

Попытка
    Сумма = Число(Фрагмент);
Исключение
    Сумма = 0; // не число — подставляем значение по умолчанию
КонецПопытки;

Так код не падает на «грязных» данных: пустых строках, тексте без цифр, лишних пробелах. Если нужно отличить настоящий ноль от ошибки преобразования, подставляйте Неопределено вместо 0 и проверяйте результат отдельно.

Частая засада — разделители. Число("1 250,50") выдаст ошибку из-за пробела-разделителя тысяч и запятой. Перед преобразованием такие строки чистят:

Очищенная = СтрЗаменить(Фрагмент, " ", "");      // убрали пробелы
Очищенная = СтрЗаменить(Очищенная, ",", ".");    // запятую — на точку
Сумма = Число(Очищенная);

Проверить тип уже полученного значения помогает ТипЗнч: условие ТипЗнч(Значение) = Тип("Число") истинно, если в переменной лежит число, а не строка. Это пригодится, когда данные приходят из внешнего источника и тип заранее неизвестен.

Ещё одна тонкость — ведущие нули. Преобразование Число("007") вернёт 7: нули в начале теряются. Если это артикул или код счёта, где нули значимы, храните и сравнивайте такие значения как строку, а не как число.

Соберём приёмы вместе на типовой задаче — вытащить сумму из назначения платежа:

Назначение = "Оплата по счёту № 1250 на сумму 18 500,00 руб, НДС 20%";
РегВыр = Новый РегулярноеВыражение("\d[\d ]*[.,]\d{2}");
Список = РегВыр.НайтиСовпадения(Назначение);
Если Список.Количество() > 0 Тогда
    Текст = СтрЗаменить(Список[0].Значение, " ", "");
    Сумма = Число(СтрЗаменить(Текст, ",", ".")); // 18500.00
КонецЕсли;

Здесь шаблон ищет число с дробной частью и допустимыми пробелами-разделителями, а чистка перед Число() убирает пробелы и меняет запятую на точку. Номер счёта 1250 и ставку 20 шаблон с обязательной дробной частью не захватит — именно это нам и нужно.

Поиск чисел в запросах к базе данных

Когда нужно не разобрать одну строку, а отобрать из справочника или документа все записи, где в текстовом поле есть цифры, задачу решают в запросе. Язык запросов 1С поддерживает в операторе ПОДОБНО диапазоны символов в квадратных скобках.

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

Шаблон %[0-9]% читается так: любой текст, затем одна цифра из диапазона 0–9, затем снова любой текст. То есть запрос вернёт все наименования, где встречается хотя бы одна цифра. Чтобы, наоборот, отобрать строки без цифр, используют НЕ … ПОДОБНО.

Если текстовое поле целиком содержит число и его надо получить именно числом, применяют ВЫРАЗИТЬ:

ВЫРАЗИТЬ(Номенклатура.Артикул КАК ЧИСЛО(15, 2))

ВЫРАЗИТЬ … КАК ЧИСЛО сработает, только если поле целиком является числом. На строке вроде Арт-45 запрос завершится ошибкой. Для смешанного текста извлекайте число в коде после выборки, а не в самом запросе.

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

Производительность на больших объёмах

Пока строка одна, скорость не важна. Но если вы обрабатываете десятки тысяч наименований или длинные тексты, пара приёмов экономит время ощутимо.

  1. Не склеивайте строку в цикле. Выражение Результат = Результат + Символ на каждой итерации создаёт новую строку, и на длинном тексте время растёт квадратично. Собирайте символы в массив и соедините один раз через СтрСоединить — как в функции ВытащитьЦифры выше.
  2. Проверяйте цифру по коду, а не через Сред. КодСимвола(Строка, Поз) не выделяет память под односимвольную подстроку, тогда как Сред(Строка, Поз, 1) создаёт новый объект на каждом шаге.
  3. Фильтруйте в запросе. Прежде чем перебирать тысячи записей в коде, отсеките лишние условием ПОДОБНО "%[0-9]%". Обрабатывать 200 отобранных строк дешевле, чем 50 000.
  4. Компилируйте регулярное выражение один раз. Создавайте объект РегулярноеВыражение до цикла по данным, а не внутри него — тогда шаблон разбирается однократно.

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

Для сравнения приёмов по задачам:

СпособКогда применятьОграничение
СтрНайтиизвестно, какое число ищемнужно знать число заранее
Перебор КодСимволавытащить любые цифры, любая версиякод длиннее
РегулярноеВыражениемного чисел, сложный форматтолько 8.3.23 и новее
Запрос ПОДОБНОотобрать записи в базене извлекает само число

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

Как вытащить из строки дробное число с запятой или точкой?
Регулярным выражением с шаблоном \d+[.,]\d+ — оно поймает и 12.5, и 12,5. Перед преобразованием в тип Число замените запятую на точку функцией СтрЗаменить, иначе Число() выдаст ошибку.
Почему `Число("1 250,50")` вызывает ошибку?
Из-за пробела-разделителя тысяч и запятой. Сначала уберите пробелы и замените запятую на точку: Число(СтрЗаменить(СтрЗаменить(Строка, " ", ""), ",", ".")).
Чем `СтрНайти` отличается от старой функции `Найти`?
Найти оставлена для совместимости и ищет только первое вхождение с начала. СтрНайти умеет искать с конца (НаправлениеПоиска.СКонца), задавать начальную позицию и находить N-е вхождение. В новом коде используйте СтрНайти.
Регулярные выражения не работают — в чём дело?
Скорее всего, платформа старше 8.3.23: встроенного объекта РегулярноеВыражение там нет. Проверьте версию в меню Справка → О программе и, если обновить платформу нельзя, используйте перебор символов.
Как получить число из строки, где несколько чисел подряд?
Регулярное выражение \d+ вернёт массив всех групп цифр — переберите его и возьмите нужную по индексу. Перебор символов со склейкой всех цифр здесь не подойдёт: он соединит разные числа в одно.