VIMScript 调整字幕时间

问题的来由

前段时间看一片电影的时候,字幕和画面对不上,延后了十几秒钟,这样看起来是很不爽的。这应该是经常出现的事情,一些视频播放器也有调整字幕的出现时间的功能,但是我用的这个播放器没有这个功能。如果为了看个电影就另外下一个有调节字幕时间功能的播放器,但这不是我的风格。

这时,我撇开电影不看,开始对那个字幕文件感兴趣了。

需要做什么

这字幕文件的结构是这样子的:

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

©版权所有,除非注明, 永在路上文章均为原创,转载请以链接形式注明出处和作者细信息。

发表评论

电子邮件地址不会被公开。 必填项已用*标注