При работе с данными в 1С мы часто сталкиваемся с необходимостью извлекать конкретные значения из текстовых строк, которые могут быть сформированы по разным шаблонам. Рассмотрим ситуацию, когда у нас есть строка вида "Товар 1000*100*10", и наша задача состоит в том, чтобы присвоить значения "1000" и "100" переменным Параметр1 и Параметр2 соответственно. Эта задача является типичной для многих сфер автоматизации, где данные поступают в полуструктурированном текстовом формате. Мы проанализируем различные подходы к решению этой проблемы, начиная от простых строковых функций и заканчивая мощными регулярными выражениями.
Выбор метода для извлечения данных из строки в 1С во многом зависит от степени стандартизации и предсказуемости структуры исходной строки. Если шаблон строки достаточно стабилен и содержит четкие разделители, то простые функции могут быть эффективными. Однако, если строки максимально разнообразны и не имеют строгого шаблона, нам потребуются более гибкие и мощные инструменты.
Один из наиболее очевидных и часто используемых методов для разделения строки на части в 1С – это функция СтрРазделить(). Этот подход эффективен, когда у нас есть явные и однозначные разделители между необходимыми частями строки. Мы можем разбить задачу на несколько этапов, последовательно применяя СтрРазделить() с разными разделителями.
Давайте разберем по шагам, как мы можем применить функцию СтрРазделить() для нашей задачи, когда исходная строка выглядит как "Товар 1000*100*10":
Деление по первому разделителю (пробел): На первом этапе мы разделяем исходную строку по пробелу. Предполагается, что наименование товара является первым элементом, а все числовые параметры находятся после первого пробела в одной подстроке, разделенной символом звездочки.
Например, из строки "Товар 1000*100*10" мы получим массив, где первым элементом будет "Товар", а вторым – "1000*100*10".
Деление по второму разделителю (звездочка): Затем мы берем второй элемент полученного массива (т.е. "1000*100*10") и применяем к нему функцию СтрРазделить(), используя в качестве разделителя символ звездочки ("*").
В результате этого шага мы получим новый массив, содержащий "1000", "100" и "10" как отдельные элементы.
Присвоение значений: Теперь, когда числовые значения находятся в отдельных элементах массива, мы можем легко присвоить их нашим переменным Параметр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" – здесь "со звездочкой" будет разбито. Таким образом, этот метод хорош только при очень строгом и неизменном шаблоне исходных данных.
Для более сложных, но при этом стандартизированных шаблонов, когда простые разделители не справляются с неоднозначностью, на помощь приходят регулярные выражения. Это мощный инструмент для поиска, извлечения и замены информации по сложному текстовому шаблону. В 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);
Иначе
Сообщить("Недостаточно захватывающих групп для извлечения параметров.");
КонецЕсли;
Иначе
Сообщить("Шаблон регулярного выражения не найден в исходной строке.");
КонецЕсли;
Разберем синтаксис шаблона регулярных выражений подробнее:
. (точка) – соответствует любому символу, кроме символа новой строки.+ (плюс) – квантификатор, означает "один или более" повторений предыдущего символа или группы. Например, \d+ означает "одна или более цифр".* (звездочка) – квантификатор, означает "ноль или более" повторений. В отличие от +, может отсутствовать.? (вопросительный знак) – квантификатор, означает "ноль или одно" повторение.\d – класс символов, соответствующий любой цифре (от 0 до 9).\* – экранированный символ звездочки. Так как звездочка является специальным символом в регулярных выражениях, для того чтобы найти именно символ звездочки в тексте, его нужно экранировать обратным слэшем. В 1С-строках обратный слэш сам является специальным символом, поэтому его нужно экранировать еще раз, получая \\*.( ) (круглые скобки) – создают захватывающую группу. Все, что находится внутри скобок, будет выделено как отдельный элемент при вызове метода ПолучитьГруппы().Пример шаблона для более гибких случаев:
Если последний параметр в строке может быть без последующей звездочки, или после него могут идти другие символы, мы можем изменить шаблон. Например, для строки "Товар 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].Строка));
КонецЕсли;
"Теперь у них две проблемы"
В контексте регулярных выражений часто цитируют шутку: "Некоторые люди, столкнувшись с проблемой, думают: «Я знаю, я воспользуюсь регулярными выражениями». Теперь у них две проблемы." Это высказывание подчеркивает, что порог входа в регулярные выражения довольно высок, и для многих программистов их синтаксис и логика могут быть сложными для понимания и отладки. Нередко некорректно составленное регулярное выражение может работать не так, как ожидалось, или быть неэффективным, создавая дополнительные проблемы вместо решения исходной. Однако, при должном изучении и практике, регулярные выражения становятся незаменимым и мощным инструментом в арсенале разработчика.
Если структура строки максимально разнородна, не поддается простым разделителям и даже регулярные выражения оказываются слишком сложными или неэффективными из-за отсутствия четких шаблонов, мы можем применить пошаговый парсинг с использованием базовых строковых функций 1С. Этот подход требует более детальной логики и зачастую написания большего объема кода, но позволяет наиболее гибко обрабатывать исключения и нестандартные форматы.
Давайте рассмотрим основные строковые функции, которые мы можем использовать для такого пошагового парсинга:
Найти(Строка, ПодстрокаПоиска, [НачальнаяПозиция], [Направление]): Эта функция возвращает позицию первого вхождения указанной подстроки в исходной строке. Она критически важна для определения местоположения наших разделителей, даже если они нерегулярны.
Например, Найти("Товар 1000*100*10", " ") вернет 6 (позиция первого пробела).
Сред(Строка, НачальнаяПозиция, [Длина]): Извлекает подстроку из исходной строки, начиная с указанной позиции и заданной длины. Если длина не указана, извлекает до конца строки. Мы будем использовать ее для "вырезания" нужных частей строки.
Например, Сред("Товар 1000*100*10", 7) (после первого пробела) вернет "1000*100*10".
Лев(Строка, КоличествоСимволов): Возвращает указанное количество символов с начала строки.
Например, если у нас есть "1000*100*10" и мы знаем, что первая звездочка на 5-й позиции, то Лев("1000*100*10", 4) вернет "1000".
Прав(Строка, КоличествоСимволов): Возвращает указанное количество символов с конца строки.
СтрДлина(Строка): Возвращает длину строки. Полезно для расчета позиций и длин подстрок.
Посмотрим на пример логики, которая использует эти функции для нашей исходной строки "Товар 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С существуют также специализированные функции, которые могут быть полезны для "очистки" извлеченных подстрок, если они могут содержать нечисловые символы:
ПолучитьСтрокуТолькоЦифры(ИсходнаяСтрока): Эта функция возвращает новую строку, содержащую только числовые символы из исходной строки, игнорируя буквы, пробелы, знаки препинания и другие нечисловые символы. Она может быть полезна, если после извлечения подстроки с параметром вы получаете "1000 руб." и хотите оставить только "1000".
ПолучитьСтрокуТолькоПервыеЦифры(ИсходнаяСтрока): Возвращает строку из первых последовательных цифр, найденных в начале строки. Если строка начинается с цифр, функция вернет эту числовую последовательность. Если строка начинается не с цифр, вернет пустую строку. Это может быть полезно, если числовой параметр всегда находится в начале числовой части строки.
Посмотрим, как эти функции могут работать:
СтрокаСМешаннымТекстом1 = "Арт.123/45Б67";
ТолькоЦифры = ПолучитьСтрокуТолькоЦифры(СтрокаСМешаннымТекстом1); // Результат: "1234567"
Сообщить("ТолькоЦифры: " + ТолькоЦифры);
СтрокаСМешаннымТекстом2 = "123Арт/45Б67";
ПервыеЦифры = ПолучитьСтрокуТолькоПервыеЦифры(СтрокаСМешаннымТекстом2); // Результат: "123"
Сообщить("ПервыеЦифры: " + ПервыеЦифры);
СтрокаСМешаннымТекстом3 = "Арт123";
ПервыеЦифрыИзНечисла = ПолучитьСтрокуТолькоПервыеЦифры(СтрокаСМешаннымТекстом3); // Результат: ""
Сообщить("ПервыеЦифрыИзНечисла: " + ПервыеЦифрыИзНечисла);
// Применение в нашем случае (если Параметр1 могли бы быть "1000м" вместо "1000"):
// ИзвлеченнаяПодстрока = "1000м";
// Параметр1 = Число(ПолучитьСтрокуТолькоЦифры(ИзвлеченнаяПодстрока)); // 1000
Эти функции не заменяют парсинг по разделителям, но могут служить дополнительным инструментом для нормализации или очистки извлеченных строковых значений перед их преобразованием в числа.
Определить, какой метод лучше всего подходит для вашей конкретной задачи, можно, проанализировав структуру и вариативность исходных строк:
Функция `СтрРазделить()`:
Регулярные выражения:
Пошаговый парсинг с базовыми строковыми функциями (`Найти`, `Сред`, `Лев` и т.д.):
В заключение, не существует универсального "лучшего" способа для всех случаев. Важно тщательно проанализировать структуру ваших исходных данных и выбрать инструмент, который обеспечит наибольшую надежность и эффективность для вашей конкретной ситуации. Для начала всегда пробуйте самые простые решения, и только при их неэффективности переходите к более сложным. Критически важно всегда тестировать выбранный метод на максимально разнообразной выборке реальных данных, чтобы убедиться в его корректной работе во всех возможных сценариях.