使用正则表达式解析.srt文件
我正在用Python写一个小脚本,但因为我还很新手,所以在某个部分卡住了:
我需要从一个.srt
文件中提取时间和文本。例如,从下面的内容中:
1
00:00:01,000 --> 00:00:04,074
Subtitles downloaded from www.OpenSubtitles.org
我需要提取:
00:00:01,000 --> 00:00:04,074
还有
从www.OpenSubtitles.org下载的字幕
。
我已经成功写出了提取时间的正则表达式,但在提取文本时遇到了困难。我尝试使用向后查找,在我提取时间的正则表达式中使用:
( ?<=(\d+):(\d+):(\d+)(?:\,)(\d+) --> (\d+):(\d+):(\d+)(?:\,)(\d+) )\w+
但没有效果。个人觉得使用向后查找是解决这个问题的正确方法,但我不太确定怎么写才对。有人能帮帮我吗?谢谢。
6 个回答
上面提到的纯正则表达式解决方案在实际的srt文件中都没能奏效。
我们来看一下下面这种格式的SRT文本:
1
00:02:17,440 --> 00:02:20,375
Some multi lined text
This is a second line
2
00:02:20,476 --> 00:02:22,501
as well as a single line
3
00:03:20,476 --> 00:03:22,501
should be able to parse unicoded text too
こんにちは
请注意:
- 文本可能包含Unicode字符。
- 文本可以由多行组成。
- 每个提示(cue)都是以一个整数开头,并以一个空行结束,支持Unix和Windows两种换行方式。
这里是有效的正则表达式:
\d+[\r\n](\d+:\d+:\d+,\d+ --> \d+:\d+:\d+,\d+)[\r\n]((.+\r?\n)+(?=(\r?\n)?))
感谢@roippi提供这个优秀的解析器。它帮了我很多,让我在不到40行代码的情况下写出了一个srt转stl的转换器(不过是用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)) )
数字:^[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]+*
希望这能帮到你。
我不同意@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
老实说,我觉得用正则表达式来解决这个问题没什么必要。.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.'])]