Как извлечь числовые параметры из строки в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с данными в 1С мы часто сталкиваемся с необходимостью извлекать конкретные значения из текстовых строк, которые могут быть сформированы по разным шаблонам. Рассмотрим ситуацию, когда у нас есть строка вида "Товар 1000*100*10", и наша задача состоит в том, чтобы присвоить значения "1000" и "100" переменным Параметр1 и Параметр2 соответственно. Эта задача является типичной для многих сфер автоматизации, где данные поступают в полуструктурированном текстовом формате. Мы проанализируем различные подходы к решению этой проблемы, начиная от простых строковых функций и заканчивая мощными регулярными выражениями.

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

Подход 1: Последовательное применение функции СтрРазделить

Один из наиболее очевидных и часто используемых методов для разделения строки на части в 1С – это функция СтрРазделить(). Этот подход эффективен, когда у нас есть явные и однозначные разделители между необходимыми частями строки. Мы можем разбить задачу на несколько этапов, последовательно применяя СтрРазделить() с разными разделителями.

Давайте разберем по шагам, как мы можем применить функцию СтрРазделить() для нашей задачи, когда исходная строка выглядит как "Товар 1000*100*10":

  1. Деление по первому разделителю (пробел): На первом этапе мы разделяем исходную строку по пробелу. Предполагается, что наименование товара является первым элементом, а все числовые параметры находятся после первого пробела в одной подстроке, разделенной символом звездочки.

    Например, из строки "Товар 1000*100*10" мы получим массив, где первым элементом будет "Товар", а вторым – "1000*100*10".

  2. Деление по второму разделителю (звездочка): Затем мы берем второй элемент полученного массива (т.е. "1000*100*10") и применяем к нему функцию СтрРазделить(), используя в качестве разделителя символ звездочки ("*").

    В результате этого шага мы получим новый массив, содержащий "1000", "100" и "10" как отдельные элементы.

  3. Присвоение значений: Теперь, когда числовые значения находятся в отдельных элементах массива, мы можем легко присвоить их нашим переменным Параметр1 и Параметр2, не забыв преобразовать строковые значения в числовой тип с помощью функции Число().

Посмотрим на пример кода, реализующего этот подход:


ИсходнаяСтрока = "Товар 1000*100*10";

// Шаг 1: Разделяем строку по пробелу
МассивПоПробелу = СтрРазделить(ИсходнаяСтрока, " ");

// Проверяем, что массив содержит как минимум два элемента
Если МассивПоПробелу.Количество() > 1 Тогда
    // Получаем часть строки с параметрами: "1000*100*10"
    СтрокаСПараметрами = МассивПоПробелу[1]; 
    
    // Шаг 2: Разделяем строку с параметрами по звездочке
    МассивПоЗвездочке = СтрРазделить(СтрокаСПараметрами, "*");
    
    // Проверяем, что массив с параметрами содержит достаточно элементов
    Если МассивПоЗвездочке.Количество() >= 2 Тогда
        // Шаг 3: Присваиваем значения переменным, преобразуя их в числовой тип
        Параметр1 = Число(МассивПоЗвездочке[0]); // Результат: 1000
        Параметр2 = Число(МассивПоЗвездочке[1]); // Результат: 100
        
        Сообщить("Параметр1: " + Параметр1);
        Сообщить("Параметр2: " + Параметр2);
        
        // Если нужен третий параметр, мы также можем его получить:
        // Параметр3 = Число(МассивПоЗвездочке[2]); // Результат: 10
        // Сообщить("Параметр3: " + Параметр3);
    Иначе
        Сообщить("Недостаточно числовых параметров после разделения по звездочке.");
    КонецЕсли;
Иначе
    Сообщить("Строка не содержит пробела или имеет некорректный формат.");
КонецЕсли;

Ограничения подхода с последовательным `СтрРазделить`

Этот метод хорошо работает для предсказуемых структур. Однако, он имеет критические ограничения, если наименование товара или другие части строки могут содержать разделители, которые мы используем (пробелы или звездочки). Например, если строка будет "Товар Продукт 1000*100*10", то при разделении по первому пробелу мы получим "Товар" и "Продукт 1000*100*10", что приведет к некорректному результату. Если наименование товара будет "Товар * супер" или "Товар *1000*100*10", это также приведет к сбоям в логике.

Расширенные возможности `СтрРазделить` с несколькими разделителями

Функция СтрРазделить() в 1С позволяет использовать несколько разделителей в одном вызове, передав их одной строкой. Например, мы можем указать " *", чтобы разделить строку как по пробелу, так и по звездочке одновременно. Это может показаться удобным, но требует особой осторожности.

Рассмотрим, как это выглядит в коде:


ИсходнаяСтрока = "Товар 1000*100*10";
// Разделяем по пробелу и по звездочке одновременно
МассивДанных = СтрРазделить(ИсходнаяСтрока, " *"); 

// Содержимое МассивДанных будет: {"Товар", "1000", "100", "10"}
// В этом случае, если нам нужны два последних параметра:
Если МассивДанных.Количество() >= 3 Тогда
    // Мы берем элементы с конца, чтобы учесть возможное переменное количество слов в наименовании.
    // Если нам нужны Параметр1 (1000) и Параметр2 (100)
    Параметр1 = Число(МассивДанных[МассивДанных.Количество() - 3]); // Получим "1000"
    Параметр2 = Число(МассивДанных[МассивДанных.Количество() - 2]); // Получим "100"

    Сообщить("Параметр1: " + Параметр1);
    Сообщить("Параметр2: " + Параметр2);
    
    // Если нужен третий параметр:
    // Параметр3 = Число(МассивДанных[МассивДанных.Количество() - 1]); // Получим "10"
    // Сообщить("Параметр3: " + Параметр3);
Иначе
    Сообщить("Недостаточно элементов в строке после разделения.");
КонецЕсли;

// Пример с наименованием, содержащим пробел:
ИсходнаяСтрока2 = "Молоко 3.2% 1000*100*10";
МассивДанных2 = СтрРазделить(ИсходнаяСтрока2, " *"); 
// Содержимое МассивДанных2: {"Молоко", "3.2%", "1000", "100", "10"}
// Итог: Параметр1 = 1000, Параметр2 = 100, что корректно для этой структуры.

Несмотря на кажущуюся гибкость, этот подход также имеет критический недостаток: он строго запрещает использование символов-разделителей (пробел или звездочка) в тех частях строки, которые не должны быть разделены. Например, если наименование товара будет "Товар со *", то это приведет к некорректному разделению. Если бы мы взяли строку "Товар 1000*100*10" и использовали разделители " *", а потом брали 3-й и 2-й элементы с конца, это могло бы быть рискованно, если количество слов в наименовании переменное или если сам товар содержит звездочку. Например, "Товар со звездочкой 1000*100*10" – здесь "со звездочкой" будет разбито. Таким образом, этот метод хорош только при очень строгом и неизменном шаблоне исходных данных.

Подход 2: Мощь регулярных выражений

Для более сложных, но при этом стандартизированных шаблонов, когда простые разделители не справляются с неоднозначностью, на помощь приходят регулярные выражения. Это мощный инструмент для поиска, извлечения и замены информации по сложному текстовому шаблону. В 1С, начиная с версии платформы 8.3.23, появилась встроенная поддержка регулярных выражений на встроенном языке с помощью функций типа СтрНайтиПоРегулярномуВыражению(), что значительно упрощает работу с ними.

Ключевым моментом для извлечения нескольких параметров является использование захватывающих групп (Capturing Groups). Это части шаблона регулярного выражения, которые заключены в круглые скобки. Метод ПолучитьГруппы(), вызванный после успешного выполнения СтрНайтиПоРегулярномуВыражению(), позволяет получить массив найденных подстрок для каждой захватывающей группы.

Рассмотрим пример, предложенный в обсуждении, для строки "Товар 1000*100*10":


ИсходнаяСтрока = "Товар 1000*100*10";

// Шаблон регулярного выражения:
// .+       - соответствует любым символам (кроме символов новой строки), один или более раз. 
//            Это позволяет "пропустить" наименование товара любой длины.
// (\\d+)   - первая захватывающая группа: соответствует одной или более цифрам. 
//            Это наш Параметр1 (1000). Мы используем двойной обратный слэш \\, 
//            потому что одинарный \ является спецсимволом в строках 1С и должен быть экранирован.
// \\*      - соответствует символу звездочки. Звездочка * является спецсимволом в регулярных выражениях 
//            (означает "ноль или более повторений"), поэтому ее нужно экранировать с помощью \\.
// (\\d+)   - вторая захватывающая группа: одна или более цифр. Это наш Параметр2 (100).
// \\*      - еще одна экранированная звездочка.
// (\\d+)   - третья захватывающая группа (если нужна): одна или более цифр. Это наш Параметр3 (10).
// (.*)     - можно добавить в конце, если последней звездочки может не быть, 
//            или если после последнего параметра могут быть другие символы.

Шаблон = ".+(\\d+)\\*(\\d+)\\*(\\d+)"; // В этом примере шаблон ищет строго 3 числа, разделенные звездочками.

РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ИсходнаяСтрока, Шаблон);

Если РезультатПоиска.Найдено Тогда
    // Если шаблон найден, получаем захваченные группы
    Группы = РезультатПоиска.ПолучитьГруппы();
    
    // Группы[0] соответствует первой захватывающей группе (\\d+), то есть "1000"
    // Группы[1] соответствует второй захватывающей группе (\\d+), то есть "100"
    // Группы[2] соответствует третьей захватывающей группе (\\d+), то есть "10"

    Если Группы.Количество() >= 2 Тогда
        Параметр1 = Число(Группы[0].Строка); // Результат: 1000
        Параметр2 = Число(Группы[1].Строка); // Результат: 100
        
        Сообщить("Параметр1: " + Параметр1);
        Сообщить("Параметр2: " + Параметр2);
        
        // Если нужен третий параметр:
        // Параметр3 = Число(Группы[2].Строка); // Результат: 10
        // Сообщить("Параметр3: " + Параметр3);
    Иначе
        Сообщить("Недостаточно захватывающих групп для извлечения параметров.");
    КонецЕсли;
Иначе
    Сообщить("Шаблон регулярного выражения не найден в исходной строке.");
КонецЕсли;

Разберем синтаксис шаблона регулярных выражений подробнее:

Пример шаблона для более гибких случаев:

Если последний параметр в строке может быть без последующей звездочки, или после него могут идти другие символы, мы можем изменить шаблон. Например, для строки "Товар 1000*100" или "Товар 1000*100 и еще что-то" мы можем использовать:


ИсходнаяСтрока1 = "Товар 1000*100";
ИсходнаяСтрока2 = "Наименование товара 1000*100 и что-то еще";
ИсходнаяСтрока3 = "МойТовар 1000*100*1000";

// Шаблон, который ищет две группы цифр, разделенные звездочкой, 
// после которых может идти что угодно или ничего.
ШаблонГибкий = ".+(\\d+)\\*(\\d+)(.*)"; 

РезультатПоиска1 = СтрНайтиПоРегулярномуВыражению(ИсходнаяСтрока1, ШаблонГибкий);
Если РезультатПоиска1.Найдено Тогда
    Группы = РезультатПоиска1.ПолучитьГруппы();
    // Группы[0] = "1000", Группы[1] = "100", Группы[2] = "" (пустая строка, так как после 100 ничего нет)
    Сообщить("Гибкий (1): Параметр1 = " + Число(Группы[0].Строка) + ", Параметр2 = " + Число(Группы[1].Строка));
КонецЕсли;

РезультатПоиска2 = СтрНайтиПоРегулярномуВыражению(ИсходнаяСтрока2, ШаблонГибкий);
Если РезультатПоиска2.Найдено Тогда
    Группы = РезультатПоиска2.ПолучитьГруппы();
    // Группы[0] = "1000", Группы[1] = "100", Группы[2] = " и что-то еще"
    Сообщить("Гибкий (2): Параметр1 = " + Число(Группы[0].Строка) + ", Параметр2 = " + Число(Группы[1].Строка));
КонецЕсли;

РезультатПоиска3 = СтрНайтиПоРегулярномуВыражению(ИсходнаяСтрока3, ШаблонГибкий);
Если РезультатПоиска3.Найдено Тогда
    Группы = РезультатПоиска3.ПолучитьГруппы();
    // Группы[0] = "1000", Группы[1] = "100", Группы[2] = "*1000"
    Сообщить("Гибкий (3): Параметр1 = " + Число(Группы[0].Строка) + ", Параметр2 = " + Число(Группы[1].Строка));
КонецЕсли;

"Теперь у них две проблемы"

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

Подход 3: Пошаговый парсинг с базовыми строковыми функциями

Если структура строки максимально разнородна, не поддается простым разделителям и даже регулярные выражения оказываются слишком сложными или неэффективными из-за отсутствия четких шаблонов, мы можем применить пошаговый парсинг с использованием базовых строковых функций 1С. Этот подход требует более детальной логики и зачастую написания большего объема кода, но позволяет наиболее гибко обрабатывать исключения и нестандартные форматы.

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

  1. Найти(Строка, ПодстрокаПоиска, [НачальнаяПозиция], [Направление]): Эта функция возвращает позицию первого вхождения указанной подстроки в исходной строке. Она критически важна для определения местоположения наших разделителей, даже если они нерегулярны.

    Например, Найти("Товар 1000*100*10", " ") вернет 6 (позиция первого пробела).

  2. Сред(Строка, НачальнаяПозиция, [Длина]): Извлекает подстроку из исходной строки, начиная с указанной позиции и заданной длины. Если длина не указана, извлекает до конца строки. Мы будем использовать ее для "вырезания" нужных частей строки.

    Например, Сред("Товар 1000*100*10", 7) (после первого пробела) вернет "1000*100*10".

  3. Лев(Строка, КоличествоСимволов): Возвращает указанное количество символов с начала строки.

    Например, если у нас есть "1000*100*10" и мы знаем, что первая звездочка на 5-й позиции, то Лев("1000*100*10", 4) вернет "1000".

  4. Прав(Строка, КоличествоСимволов): Возвращает указанное количество символов с конца строки.

  5. СтрДлина(Строка): Возвращает длину строки. Полезно для расчета позиций и длин подстрок.

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


ИсходнаяСтрока = "Товар 1000*100*10";
Параметр1 = Неопределено;
Параметр2 = Неопределено;

// 1. Ищем позицию первого пробела, чтобы отделить наименование от параметров
ПозицияПробела = Найти(ИсходнаяСтрока, " ");

Если ПозицияПробела > 0 Тогда
    // Получаем подстроку, содержащую параметры: "1000*100*10"
    СтрокаСПараметрами = Сред(ИсходнаяСтрока, ПозицияПробела + 1); 
    
    // 2. Ищем позицию первой звездочки в строке с параметрами
    ПозицияПервойЗвездочки = Найти(СтрокаСПараметрами, "*");
    
    Если ПозицияПервойЗвездочки > 0 Тогда
        // Извлекаем Параметр1 (часть строки до первой звездочки)
        Параметр1 = Число(Лев(СтрокаСПараметрами, ПозицияПервойЗвездочки - 1)); // Результат: 1000
        Сообщить("Параметр1: " + Параметр1);
        
        // Получаем оставшуюся часть строки после первой звездочки: "100*10"
        СтрокаОстатокПослеПервогоПараметра = Сред(СтрокаСПараметрами, ПозицияПервойЗвездочки + 1);
        
        // 3. Ищем позицию второй звездочки в оставшейся строке
        ПозицияВторойЗвездочки = Найти(СтрокаОстатокПослеПервогоПараметра, "*");
        
        Если ПозицияВторойЗвездочки > 0 Тогда
            // Извлекаем Параметр2 (часть строки до второй звездочки)
            Параметр2 = Число(Лев(СтрокаОстатокПослеПервогоПараметра, ПозицияВторойЗвездочки - 1)); // Результат: 100
            Сообщить("Параметр2: " + Параметр2);
            
            // Если нужен третий параметр, он будет после второй звездочки
            // СтрокаОстатокПослеВторогоПараметра = Сред(СтрокаОстатокПослеПервогоПараметра, ПозицияВторойЗвездочки + 1);
            // Параметр3 = Число(СтрокаОстатокПослеВторогоПараметра); // Результат: 10
            // Сообщить("Параметр3: " + Параметр3);
        Иначе
            Сообщить("Вторая звездочка не найдена, возможно, только один параметр.");
            // Если строка имела вид "1000*100", то здесь Параметр2 мог бы быть равен Число(СтрокаОстатокПослеПервогоПараметра)
            Попытка
                Параметр2 = Число(СтрокаОстатокПослеПервогоПараметра);
                Сообщить("Параметр2 (предположительно): " + Параметр2);
            Исключение
                Сообщить("Не удалось извлечь второй параметр.");
            КонецПопытки;
        КонецЕсли;
    Иначе
        Сообщить("Звездочка для разделения параметров не найдена.");
    КонецЕсли;
Иначе
    Сообщить("Пробел для отделения наименования от параметров не найден.");
КонецЕсли;

Дополнительные полезные функции для извлечения только чисел:

В 1С существуют также специализированные функции, которые могут быть полезны для "очистки" извлеченных подстрок, если они могут содержать нечисловые символы:

  1. ПолучитьСтрокуТолькоЦифры(ИсходнаяСтрока): Эта функция возвращает новую строку, содержащую только числовые символы из исходной строки, игнорируя буквы, пробелы, знаки препинания и другие нечисловые символы. Она может быть полезна, если после извлечения подстроки с параметром вы получаете "1000 руб." и хотите оставить только "1000".

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

Посмотрим, как эти функции могут работать:


СтрокаСМешаннымТекстом1 = "Арт.123/45Б67";
ТолькоЦифры = ПолучитьСтрокуТолькоЦифры(СтрокаСМешаннымТекстом1); // Результат: "1234567"
Сообщить("ТолькоЦифры: " + ТолькоЦифры);

СтрокаСМешаннымТекстом2 = "123Арт/45Б67";
ПервыеЦифры = ПолучитьСтрокуТолькоПервыеЦифры(СтрокаСМешаннымТекстом2); // Результат: "123"
Сообщить("ПервыеЦифры: " + ПервыеЦифры);

СтрокаСМешаннымТекстом3 = "Арт123";
ПервыеЦифрыИзНечисла = ПолучитьСтрокуТолькоПервыеЦифры(СтрокаСМешаннымТекстом3); // Результат: ""
Сообщить("ПервыеЦифрыИзНечисла: " + ПервыеЦифрыИзНечисла);

// Применение в нашем случае (если Параметр1 могли бы быть "1000м" вместо "1000"):
// ИзвлеченнаяПодстрока = "1000м";
// Параметр1 = Число(ПолучитьСтрокуТолькоЦифры(ИзвлеченнаяПодстрока)); // 1000

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

Выбор оптимального подхода

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

  1. Функция `СтрРазделить()`:

    • Когда использовать: Идеально подходит, если структура строки проста и строго стандартизирована, а разделители являются однозначными и никогда не встречаются в частях строки, которые не должны быть разделены. Например, если всегда есть "Слово Число*Число*Число".
    • Преимущества: Простота реализации, легко читаемый код, высокая производительность для простых задач.
    • Недостатки: Очень чувствителен к изменениям в шаблоне строки. Не подходит, если разделители (например, пробел или звездочка) могут встречаться внутри наименования товара.
  2. Регулярные выражения:

    • Когда использовать: Ваш выбор, если шаблон строки сложный, но повторяющийся, имеет множество вариаций или требует пропуска неизвестного количества символов. Если нужно извлечь несколько элементов по сложному правилу, регулярные выражения незаменимы.
    • Преимущества: Чрезвычайно мощный и гибкий инструмент для поиска и извлечения по шаблону. Позволяет игнорировать ненужные части строки, использовать захватывающие группы для получения конкретных значений. Встроенная поддержка в новых версиях 1С делает их удобными.
    • Недостатки: Высокий порог входа, сложный для освоения синтаксис. Отладка некорректно работающих шаблонов может быть трудоемкой. Может быть менее производительным для очень простых задач по сравнению со `СтрРазделить`.
  3. Пошаговый парсинг с базовыми строковыми функциями (`Найти`, `Сред`, `Лев` и т.д.):

    • Когда использовать: Если строки максимально разнородны, не имеют четкого или строгого шаблона, и вы не можете или не хотите использовать регулярные выражения из-за их сложности или отсутствия повторяемости шаблона. Также может быть предпочтительным, если вам нужна очень тонкая и специфическая логика обработки каждого возможного исключения.
    • Преимущества: Максимальная гибкость и контроль над процессом парсинга. Возможность обрабатывать уникальные случаи и исключения с помощью кастомной логики.
    • Недостатки: Требует написания большего объема кода, который может быть менее читаемым и более подвержен ошибкам при изменении требований. Производительность может снижаться при обработке очень больших строк или большого количества строк из-за множественных операций со строками.

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

← На главную