Разобрать файл субтитров с использованием регулярного выражения C#
Вопрос
Мне нужно найти номер, точки входа и выхода тайм-кода и все строки текста.
9
00:09:48,347 --> 00:09:52,818
- Let's see... what else she's got?
- Yea... ha, ha.
10
00:09:56,108 --> 00:09:58,788
What you got down there, missy?
11
00:09:58,830 --> 00:10:00,811
I wouldn't do that!
12
00:10:03,566 --> 00:10:07,047
-Shit, that's not enough!
-Pull her back!
В настоящее время я использую этот шаблон, но он забывает все две строки текста.
(?<Order>\d+)\r\n(?<StartTime>(\d\d:){2}\d\d,\d{3}) --> (?<EndTime>(\d\d:){2}\d\d,\d{3})\r\n(?<Sub>.+)(?=\r\n\r\n\d+|$)
Любая помощь приветствуется.
Решение
Я думаю, что с регулярным выражением есть две проблемы.Первое заключается в том, что .
ближе к концу в (?<Sub>.+)
не соответствует символу новой строки.Таким образом, вы можете изменить его на:
(?<Sub>(.|[\r\n])+?)
Или вы можете указать RegexOptions.Singleline
как вариант регулярного выражения.Единственное, что делает этот параметр, — это соответствие точек новой строке.
Вторая проблема в том, что .+
соответствует как можно большему количеству строк.Вы можете сделать это не жадным, например:
(?<Sub>(.|[\r\n])+?(?=\r\n\r\n|$))
Это соответствует наименьшему количеству текста, который заканчивается пустой строкой или концом строки.
Другие советы
На вашем месте я бы отошел от реализации на основе регулярных выражений и посмотрел на конечный автомат, просматривающий файл построчно.Ваш формат выглядит достаточно простым, чтобы справиться с 20-40 строками простого для понимания кода, но слишком сложен для разумного регулярного выражения.
Я бы лично разделил строки на массив и просматривал бы массив, проверяя каждую строку, просто выполняя регулярное выражение для строк StartTime->EndTime, затем вы можете использовать довольно простую логику, чтобы получить Order из предыдущей строки и получить текст из следующих строк (путем поиска вперед, чтобы найти следующее время начала-> время окончания и возврата на две строки).
Я думаю, что этот способ немного упрощает проблему, и у вас не будет регулярного выражения, пытающегося сделать все это.
Я использую следующее регулярное выражение для анализа файлов .srt:
@"(?<number>\d+)\r\n(?<start>\S+)\s-->\s(?<end>\S+)\r\n(?<text>(.|[\r\n])+?)\r\n\r\n"
Я использовал это регулярное выражение в своем парсере Ruby:
slines.scan(/(^[0-9]+)\r?\n(.*? --> .*?)\r?\n(.*?)(?=^[0-9]+\r?\n|\s+\Z)/im).map{|z| [z[0],[z[1],z[2].strip]]}
где «slines» — весь файл субтитров, считанный в память.