问题的来由
前段时间看一片电影的时候,字幕和画面对不上,延后了十几秒钟,这样看起来是很不爽的。这应该是经常出现的事情,一些视频播放器也有调整字幕的出现时间的功能,但是我用的这个播放器没有这个功能。如果为了看个电影就另外下一个有调节字幕时间功能的播放器,但这不是我的风格。
这时,我撇开电影不看,开始对那个字幕文件感兴趣了。
需要做什么
这字幕文件的结构是这样子的:
1 00:00:15,000 --> 00:00:20,033 1991年 2 00:00:05,227 --> 00:00:11,215 3 00:01:05,797 --> 00:01:08,695 NO! 4 00:01:09,171 --> 00:01:12,015 真是没有! 5 00:01:14,200 --> 00:01:15,702 渴望
我没有查过那字幕文件的内容结构,但从上面这段内容结构来看,感觉也是可以理解的。上面这段内容一共有五句字幕内容,每一句包含有三行分别表示三个信息。就拿第一句来看:
1 00:00:15,000 --> 00:00:20,033 1991年
第一行是一个整型数字,它随着字幕的增加而增加,应该是用来表示当前字幕是第一句字幕的意思;
第二行包含有两个时间格式的字符串和一个箭头,这也不难知道,它是标示字幕出现的时间段;
第三行是一个字符串,这就是字幕的内容了。
要调整字幕的时间,我们只需要关注第二行就行了。我们再来看下第二行时间的结构:
00:00:15,000 --> 00:00:20,033
第一个时间段表示字幕开始显示时间,第二个表示字幕结束显示时间,每个时间字符串有四个整型数字,分别表示时、分、秒、毫秒。我们要修改的是秒级别的数据,也就是说,如果我们需要把字幕向前调整五秒,那么我们就需要在表示秒的那个数字减 5 。
初次尝试
如果是这样子的话,在 VIM 编辑器中使用一条查找替换命令就完事了。
:%s/^\(.*\):\(\d\d\)\(.*\) --> \(.*\):\(\d\d\),\(.*\)$/\=submatch(1).":".(submatch(2) - 22).submatch(3)." --> ".submatch(4).":".(submatch(5) - 22).",".submatch(6)/g
上面这条命令的作用是,将字幕文件的所有时间向前调整 22 秒。
但这是行不通的,这条命令只是对秒级的数字进行操作,而没有考虑到它和分钟及小时的进制关系。也就是说,单单考虑操作秒级的数据,那么秒级的数据就有可能出现负数或者大于60的数,这对于一个时间的秒数来说是非法的!
所以,执行完上面这条命令之后,第一句字幕的时间变成如下:
00:00:-7,000 --> 00:00:-2,033
秒数出现负数,这是不可行的,需要改进!
进一步
很明显,单单一条命令是难以解决这个问题了,我们现在打算使用 VIM 更高级一点的功能:VIMScript。这也容易理解,当一个语句解决不了问题时,我们可以写个函数来解决。VIMScript 就是可以提供我们在 VIM 中编写函数来处理问题的能力的工具。
接下来就是写个函数咯。
文件: changeTime.vim
"let theTime = '08:23:45' "let intv = -8 function! ChangeTime(theTime,intv) let timePat = '\(\d\d\):\(\d\d\):\(\d\d\)' let beginData = matchlist(a:theTime,timePat) let beginRes = ChangeSeconds({'hour':beginData[1],'minute':beginData[2],'second':beginData[3]},a:intv) return (beginRes.hour).':'.(beginRes.minute).':'.(beginRes.second) endfunction "let timeData = {hour:0,minute:0,second:0} "let intv = 5 function! ChangeSeconds(timeData,intv) let a:timeData.second = a:timeData.second + a:intv while a:timeData.second >= 60 let a:timeData.second -= 60 let a:timeData.minute += 1 if a:timeData.minute >= 60 let a:timeData.minute -= 60 let a:timeData.hour += 1 endif endwhile while a:timeData.second < 0 let a:timeData.second += 60 let a:timeData.minute -= 1 if a:timeData.minute < 0 let a:timeData.minute += 60 let a:timeData.hour -= 1 endif endwhile "two number if a:timeData.hour < 10 && strlen(a:timeData.hour) < 2 let a:timeData.hour = '0'.(a:timeData.hour) endif if a:timeData.minute< 10 && strlen(a:timeData.minute) < 2 let a:timeData.minute= '0'.(a:timeData.minute) endif if a:timeData.second < 10 && strlen(a:timeData.second) < 2 let a:timeData.second = '0'.(a:timeData.second) endif return a:timeData endfunction
上面的代码定义了两个函数:ChangeTime()
和 ChangeSeconds()
。
ChangeTime()
需要两个参数,第一个是一个时间格式的字符串,第二个是一个整型数字。它主要是将时间字符串转换成分别表示时、分、秒的整型数字,然后将这些数字传给ChangeSeconds()
函数处理。它返回处理后的时间字符串。
ChangeSeconds()
也需要两个参数,第一个是包含时、分、秒三个键值对的字典型数据,第二个是一个整型数据。它对时间加减时的进制关系进行处理,确保时间格式的合法性,并确保每个数字都是两位数。
有了这两个函数之后,我们的查找替换命令就可以这样子写了,当然,在使用之前需要先加载包含这两个函数的脚本文件:
:source ./changeTime.vim
查找替换命令:
:%s/^\(\d\d:\d\d:\d\d\),\(.*\)\(\d\d:\d\d:\d\d\),\(.*\)$/\=ChangeTime(submatch(1),5).",".submatch(2).ChangeTime(submatch(3),5).",".submatch(4)/g
接下来就是确定电影字幕是提前还是延后了,将需要调整的数值传给 ChangeTime()
就可以了
更进一步
当然,到这里可以还没完,我们可以进一步将那个查找替换命令封装成一个函数,直接调用函数就可以了。不过我就不在继续了,我要看电影去了...
本文作者: chenishr
本文标题:《VIMScript 调整字幕时间》
本文地址: http://blog.chenishr.com/?p=424
©版权所有,除非注明, 永在路上文章均为原创,转载请以链接形式注明出处和作者细信息。