Сопоставление RegEx для синтаксиса SRT и VTT из субтитров

У меня есть субтитры как в формате srt, так и в формате vtt, где мне нужно сопоставить и удалить синтаксис, специфичный для формата, и просто получить чистые строки с текстом.

Я придумал это регулярное выражение: /\n?\d*?\n?^.* --> [012345]{2}:.*$/m

образец содержимого (смешайте как srt, так и vtt):

1
00:00:04,019 --> 00:00:07,299
line1
line2

2
00:00:07,414 --> 00:00:09,155
line1

00:00:09,276 --> 00:00:11,429
line1

00:00:11,549 --> 00:00:14,874
line1
line2

Это соответствует как номеру субтитров, так и времени, как и ожидалось, смоделировано в https://regex101.com/r/zRsRMR. /2/

Но при использовании в самом коде (даже с использованием непосредственно сгенерированного фрагмента кода из https://regex101.com) это будет соответствовать только времени, а не номеру субтитров.

Смотрите вывод:

array (5)
0 => array (1)
0 => "00:00:04,019 --> 00:00:07,299
" (30)
1 => array (1)
0 => "
00:00:07,414 --> 00:00:09,155
" (31)
2 => array (1)
0 => "
00:00:09,276 --> 00:00:11,429
" (31)
3 => array (1)
0 => "
00:00:11,549 --> 00:00:14,874
" (31)
4 => array (1)
0 => "
00:00:11,549 --> 00:00:14,874
" (31)

Можно протестировать на: http://sandbox.onlinephpfunctions.com/code/dec294251b879144f40a6d1bdd516d20420321>

Цель состоит в том, чтобы сопоставить даже номер субтитров, например, первое ожидаемое совпадение должно быть:

1
00:00:04,019 --> 00:00:07,299

person Johncze    schedule 08.05.2019    source источник


Ответы (2)


Вы можете сделать эту часть вашего выражения \n?\d*?\n? необязательной группой для соответствия 1+ цифрам, за которыми следует новая строка. Класс символов [012345] также может быть записан как [0-5].

Вы можете обновить свое выражение до:

^(?:\d+\n)?.*\h+-->\h+[0-5]{2}:.*$
  • ^ Начало строки
  • (?:\d+\n)? Дополнительные 1+ цифры и новая строка
  • .*\h+-->\h+ Match 0+ times any char except newline, 1+ horizontal whitespace chars,-->` и 1+ горизонтальных пробельных символов
  • [0-5]{2}: Совпадение 2 раза 0-5
  • .* Соответствует 0+ раз любому символу, кроме новой строки
  • $ Конец строки

демонстрация регулярных выражений | демонстрация Php

person The fourth bird    schedule 09.05.2019
comment
спасибо, в конце придумал похожее решение, которое, похоже, работает как положено: ^(?:\n?\d.*\n^)?.*\h+-->\h+[0-5]{2}:.*$ (regex101.com/r /f9M3xM/2), поскольку меня вдохновил ваш ответ, я отмечу его как ответ - person Johncze; 10.05.2019

Я не совсем уверен, может быть, это то, что вы хотели бы запечатлеть. Однако причина в том, что вы можете захотеть обернуть свою строку группами захвата, чтобы ее было легко получить. Например, это выражение показывает, как группы захвата работают с нужными символами:

^([0-9]+\n|)([0-9:,->\s]+)

введите здесь описание изображения

Возможно, это не лучший способ сделать это или лучшее выражение. Тем не менее, это может дать вам представление о другом подходе к проблеме.

Я предполагаю, что вы можете захотеть захватить строку даты и времени и строки до этого, которые могут иметь или не иметь номер.

График

На этом графике показано, как будет работать выражение, и вы можете визуализировать другие выражения в этом ссылка:

введите здесь описание изображения

Возможно, вы захотите написать скрипт для очистки ваших данных перед отправкой их в механизм RegEx, чтобы у вас было простое выражение.

Пример теста с JavaScript

const regex = /^([0-9]+\n|)([0-9:,->\s]+)/mg;
const str = `1
00:00:04,019 --> 00:00:07,299
line1
line2

2
00:00:07,414 --> 00:00:09,155
line1

00:00:09,276 --> 00:00:11,429
line1

00:00:11,549 --> 00:00:14,874
line1
line2
`;
let m;

while ((m = regex.exec(str)) !== null) {
    // This is necessary to avoid infinite loops with zero-width matches
    if (m.index === regex.lastIndex) {
        regex.lastIndex++;
    }
    
    // The result can be accessed through the `m`-variable.
    m.forEach((match, groupIndex) => {
        console.log(`Found match, group ${groupIndex}: ${match}`);
    });
}

PHP-тест

Это может не сгенерировать желаемый результат, это просто пример:

$re = '/^([0-9]+\n|)([0-9:,->\s]+)/m';
$str = '1
00:00:04,019 --> 00:00:07,299
line1
line2

2
00:00:07,414 --> 00:00:09,155
line1

00:00:09,276 --> 00:00:11,429
line1

00:00:11,549 --> 00:00:14,874
line1
line2
';

preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);

foreach ($matches[0] as $key => $value) {
    if ($value == "") {
        unset($matches[0][$key]);
    } else {
        $matches[0][$key] = trim($value);
    }

}

var_dump($matches[0]);

Тест производительности

Этот фрагмент JavaScript показывает производительность этого выражения с использованием простого цикла for 1 миллион раз.

repeat = 1000000;
start = Date.now();

for (var i = repeat; i >= 0; i--) {
	var string = '2  \n00:00:07,414 --> 00:00:09,155';
	var regex = /(.*)([0-9:,->\s]+)/gm;
	var match = string.replace(regex, "$2");
}

end = Date.now() - start;
console.log("YAAAY! \"" + match + "\" is a match ???????????? ");
console.log(end / 1000 + " is the runtime of " + repeat + " times benchmark test. ???? ");

Если вы хотите захватить весь желаемый результат в одну переменную, вы можете просто добавить группу захвата вокруг всего выражения, а затем вызвать его с помощью $1.

Вы также можете добавить или уменьшить границы, если хотите, например вот здесь.

^(?:[0-9]+\n|\n)(([0-9:,]+)([\s->]+)([0-9:,]+))$

введите здесь описание изображения

введите здесь описание изображения

Пример теста с JavaScript для второго выражения

const regex = /^(?:[0-9]+\n|\n)(([0-9:,]+)([\s->]+)([0-9:,]+))$/gm;
const str = `1
00:00:04,019 --> 00:00:07,299
- cdcdc
- cddcd

2
00:00:07,414 --> 00:00:09,155
54564

00:00:09,276 --> 00:00:11,429
- 445454 - ccd
- cdscdcdcd

00:00:11,549 --> 00:00:14,874
line1
line2
`;
let m;

while ((m = regex.exec(str)) !== null) {
    // This is necessary to avoid infinite loops with zero-width matches
    if (m.index === regex.lastIndex) {
        regex.lastIndex++;
    }
    
    // The result can be accessed through the `m`-variable.
    m.forEach((match, groupIndex) => {
        console.log(`Found match, group ${groupIndex}: ${match}`);
    });
}

person Emma    schedule 08.05.2019
comment
спасибо Эмме за подробный ответ, мне действительно не нужно использовать группу захвата, так как я просто запускаю preg_split позже, чтобы удалить совпадения и получить массив с чистыми строками, все равно попробовал ваш подход и обнаружил, что он не будет соответствовать нескольким случаям, которые я дин не указан в моем образце - обновлено здесь: regex101.com/r/XnEsvb/3 - person Johncze; 09.05.2019
comment
хорошо, выбрал некоторые идеи из вашего решения - person Johncze; 10.05.2019