разбор файла .srt с регулярным выражением

Я делаю небольшой скрипт на питоне, но, поскольку я совсем новичок, я застрял в одной части: мне нужно получить время и текст из файла .srt. Например, из

1
00:00:01,000 --> 00:00:04,074
Subtitles downloaded from www.OpenSubtitles.org

Мне нужно получить:

00:00:01,000 --> 00:00:04,074

а также

Subtitles downloaded from www.OpenSubtitles.org.

Мне уже удалось сделать регулярное выражение для синхронизации, но я застрял в тексте. Я пытался использовать look back, где я использую свое регулярное выражение для определения времени:

( ?<=(\d+):(\d+):(\d+)(?:\,)(\d+) --> (\d+):(\d+):(\d+)(?:\,)(\d+) )\w+

но без эффекта. Лично я считаю, что использование look back — правильный способ решить эту проблему, но я не уверен, как это правильно написать. Может кто-нибудь помочь мне? Спасибо.


person user3630290    schedule 12.05.2014    source источник
comment
попробуйте с: (\d\d:\d\d:\d\d,\d\d\d.+\d\d:\d\d:\d\d,\d\d\d)| (Субтитры загружены с www.OpenSubtitles.org)   -  person Federico Piazza    schedule 13.05.2014
comment
Можете ли вы добавить еще один пример субтитров и использовать теги кода (`) вместо кавычек (>)? Кроме того, можете ли вы показать часть кода Python, использующего это регулярное выражение?   -  person    schedule 13.05.2014


Ответы (5)


Честно говоря, я не вижу причин использовать регулярное выражение для решения этой проблемы. .srt файлы хорошо структурированы. Структура выглядит так:

  • целое число, начинающееся с 1, монотонно возрастающее
  • начать --› остановить отсчет времени
  • одна или более строка содержания субтитров
  • пустая строка

... и повторить. Обратите внимание на выделенную жирным шрифтом часть — возможно, вам придется захватить 1, 2 или 20 строк содержимого субтитров после тайм-кода.

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

from itertools import groupby
# "chunk" our input file, delimited by blank lines
with open(filename) as f:
    res = [list(g) for b,g in groupby(f, lambda x: bool(x.strip())) if b]

Например, используя пример на странице документа SRT, я получаю:

res
Out[60]: 
[['1\n',
  '00:02:17,440 --> 00:02:20,375\n',
  "Senator, we're making\n",
  'our final approach into Coruscant.\n'],
 ['2\n', '00:02:20,476 --> 00:02:22,501\n', 'Very good, Lieutenant.\n']]

И я мог бы далее преобразовать это в список значимых объектов:

from collections import namedtuple

Subtitle = namedtuple('Subtitle', 'number start end content')

subs = []

for sub in res:
    if len(sub) >= 3: # not strictly necessary, but better safe than sorry
        sub = [x.strip() for x in sub]
        number, start_end, *content = sub # py3 syntax
        start, end = start_end.split(' --> ')
        subs.append(Subtitle(number, start, end, content))

subs
Out[65]: 
[Subtitle(number='1', start='00:02:17,440', end='00:02:20,375', content=["Senator, we're making", 'our final approach into Coruscant.']),
 Subtitle(number='2', start='00:02:20,476', end='00:02:22,501', content=['Very good, Lieutenant.'])]
person roippi    schedule 12.05.2014
comment
Отличный пример кода здесь. Я не знал об itertools - person Jon M; 28.02.2015
comment
Ссылка не работает. Вероятно, это должно помочь: matroska.org/technical/subtitles.html #srt-субтитры - person Tuan-Tu; 10.04.2021

Не согласен с @roippi. Regex — очень хорошее решение для сопоставления текста. И регулярное выражение для этого решения не сложно.

import re   

f = file.open(yoursrtfile)
# Parse the file content
content = f.read()
# Find all result in content
# The first big (__) retrieve the timing, \s+ match all timing in between,
# The (.+) means retrieve any text content after that.
result = re.findall("(\d+:\d+:\d+,\d+ --> \d+:\d+:\d+,\d+)\s+(.+)", content)
# Just print out the result list. I recommend you do some formatting here.
print result
person nqngo    schedule 12.05.2014
comment
спасибо, у меня только еще один вопрос. Если бы мне нужно было два отдельных регулярных выражения, одно для синхронизации и одно для текста, как бы это выглядело? Мне нужно регулярное выражение только для текста, я уже выяснил время - person user3630290; 13.05.2014
comment
@user3630290 user3630290 В этом случае вам лучше всего следовать ответу @roippi. Я считаю, что Regex — очень элегантное решение, если вам нужно сопоставить определенные сегменты во всем документе, т.е. подход сверху вниз в представлении документа. Если они вам нужны в отдельных регулярных выражениях, то решение сверху вниз не будет тривиальным. Например, в очень длинном файле .srt. Как бы вы определили, является ли конкретное число 12112 маркером строки или комментарием? Конечно, вы можете сделать это с просмотром назад и просмотром вперед, но вы очень близко подходите к злоупотреблению регулярными выражениями. - person nqngo; 14.05.2014

номер:^[0-9]+$
время:
^[0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9] --> [0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9]$
строка: *[a-zA-Z]+*

надеюсь на эту помощь.

person Edw    schedule 26.01.2017

Спасибо @roippi за этот отличный парсер. Мне очень помогло написать конвертер srt в stl менее чем за 40 строк (хотя на python2, так как он должен вписываться в более крупный проект)

from __future__ import print_function, division
from itertools import groupby
from collections import namedtuple

# prepare  - adapt to you needs or use sys.argv
inputname = 'FR.srt'  
outputname = 'FR.stl'
stlheader = """
$FontName           = Arial
$FontSize           = 34
$HorzAlign          = Center
$VertAlign          = Bottom

"""
def converttime(sttime):
    "convert from srt time format (0...999) to stl one (0...25)"
    st = sttime.split(',')
    return "%s:%02d"%(st[0], round(25*float(st[1])  /1000))

# load
with open(inputname,'r') as f:
    res = [list(g) for b,g in groupby(f, lambda x: bool(x.strip())) if b]

# parse
Subtitle = namedtuple('Subtitle', 'number start end content')
subs = []
for sub in res:
    if len(sub) >= 3: # not strictly necessary, but better safe than sorry
        sub = [x.strip() for x in sub]
        number, start_end, content = sub[0], sub[1], sub[2:]   # py 2 syntax
        start, end = start_end.split(' --> ')
        subs.append(Subtitle(number, start, end, content))

# write
with open(outputname,'w') as F:
    F.write(stlheader)
    for sub in subs:
        F.write("%s , %s , %s\n"%(converttime(sub.start), converttime(sub.end), "|".join(sub.content)) )
person mad7777    schedule 13.09.2017

На время:

pattern = ("(\d{2}:\d{2}:\d{2},\d{3}?.*)")
person PersianMan    schedule 04.12.2015