Как извлечь подстроку из текста с запятыми, если внутри скобок тоже есть запятые?

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

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

Метод 1. Алгоритм «Счетчик вложенности» (посимвольный перебор)

Проанализируем наиболее надежный алгоритмический способ. Если мы не можем использовать регулярные выражения или изменить формат входных данных, нам придется обрабатывать строку символ за символом. Основная идея заключается в использовании переменной-счетчика, которая отслеживает уровень вложенности скобок. Мы будем считать запятую разделителем только в том случае, если мы находимся «снаружи» любых скобок (счетчик равен 0).

Рассмотрим реализацию этого алгоритма на языке 1С:


Функция ИзвлечьТокенСУчетомСкобок(Знач ИсходнаяСтрока, НужныйИндекс) Экспорт
    
    ТекущийИндекс = 1;
    Результат = "";
    СчетчикВложенности = 0;
    
    Для Позиция = 1 По СтрДлина(ИсходнаяСтрока) Цикл
        
        Символ = Сред(ИсходнаяСтрока, Позиция, 1);
        
        // Отслеживаем вход и выход из скобок
        Если Символ = "(" Тогда
            СчетчикВложенности = СчетчикВложенности + 1;
        ИначеЕсли Символ = ")" Тогда
            СчетчикВложенности = СчетчикВложенности - 1;
        КонецЕсли;
        
        // Проверяем, является ли символ разделителем
        Если Символ = "," И СчетчикВложенности = 0 Тогда
            ТекущийИндекс = ТекущийИндекс + 1;
            Продолжить;
        КонецЕсли;
        
        // Если мы дошли до нужного нам токена, накапливаем символы
        Если ТекущийИндекс = НужныйИндекс Тогда
            Результат = Результат + Символ;
        ИначеЕсли ТекущийИндекс > НужныйИндекс Тогда
            // Мы уже прошли нужный токен, можно выходить из цикла
            Прервать;
        КонецЕсли;
        
    КонецЦикла;
    
    Возврат СокрЛП(Результат);
    
КонецФункции

Этот метод хорош тем, что он корректно обрабатывает даже вложенные скобки, например: "Параметр, Значение(внутри(еще, глубже), и тут), Конец". Вызов ИзвлечьТокенСУчетомСкобок(Строка, 2) вернет "Значение(внутри(еще, глубже), и тут)".

Метод 2. Использование регулярных выражений (платформа 8.3.23+)

Если ваша конфигурация работает на платформе версии 8.3.23 и выше, мы можем воспользоваться встроенными функциями для работы с регулярными выражениями. Проанализируем два подхода в рамках этого метода.

Вариант А. Очистка строки от скобок. Если содержимое скобок нам не важно, мы можем просто удалить их вместе со всем содержимым, а затем применить стандартный СтрРазделить.


// Удаляем все, что находится внутри круглых скобок
Шаблон = "\([^)]*\)"; 
СтрокаБезСкобок = СтрЗаменитьПоРегулярномуВыражению(ИсходнаяСтрока, Шаблон, "");

// Теперь можем смело делить по запятой
МассивТокенов = СтрРазделить(СтрокаБезСкобок, ",");
Если МассивТокенов.Количество() >= 3 Тогда
    Результат = СокрЛП(МассивТокенов[2]); // Индексация массива начинается с 0
КонецЕсли;

Вариант Б. Поиск по паттерну. Если нам известно, что третий токен — это разрешение видео (например, 1920x1080), мы можем искать его напрямую, не привязываясь к порядковому номеру запятой. Разберем пример поиска паттерна «число-икс-число».


ШаблонРазрешения = "\d{3,5}x\d{3,5}"; // Ищем числа от 3 до 5 знаков, разделенные 'x'
РезультатыПоиска = РезультатПоискаПоРегулярномуВыражению(ИсходнаяСтрока, ШаблонРазрешения);

Если РезультатыПоиска.Количество() > 0 Тогда
    Результат = РезультатыПоиска[0].Значение;
КонецЕсли;

Метод 3. Профессиональный подход через параметры ffprobe

Выясним причину, по которой парсинг стандартного вывода ffmpeg считается «плохим тоном». Текстовый вывод предназначен для чтения человеком, и его формат может незначительно меняться от версии к версии. Гораздо надежнее заставить утилиту отдавать данные в структурированном виде.

Рассмотрим ключи ffprobe, которые избавят нас от необходимости сложного парсинга:

  1. Формат JSON: Используйте ключ -print_format json. В 1С полученную строку можно мгновенно превратить в Структура или Соответствие с помощью функции ПрочитатьJSON(). Это исключает любые ошибки с запятыми.
  2. Формат Flat: Ключ -print_format flat выведет данные в виде streams.stream.0.width=1920. Такую строку легко разбирать простым поиском подстроки или СтрРазделить по знаку равенства.
  3. Выборочный вывод: Чтобы получить только разрешение, используйте: -show_entries stream=width,height -of csv=s=x:p=0. На выходе вы получите готовую строку вида 1920x1080.

Пример вызова из 1С с использованием формата flat:


Команда = "ffprobe -v quiet -select_streams v:0 -show_entries stream=width,height -of default=noprint_wrappers=1 ""C:\video.mp4""";
// Вывод будет примерно таким:
// width=1920
// height=1080

Метод 4. Постобработка массива

Если вы предпочитаете работать со стандартными функциями, можно применить комбинированный метод. Разберем ситуацию: мы сначала разбиваем строку «в лоб» через СтрРазделить(Строка, ","), а затем объединяем элементы массива, в которых нарушен баланс скобок.

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

Важные рекомендации:

← На главную