新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   >>中国XML论坛<<     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论XSL,XSLT,XSL-FO,CSS等技术
    [返回] 中文XML论坛 - 专业的XML技术讨论区XML.ORG.CN讨论区 - XML技术『 XSL/XSLT/XSL-FO/CSS 』 → [原创]XSLT实现SRT电影字幕修正 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 8732 个阅读者浏览上一篇主题  刷新本主题   平板显示贴子 浏览下一篇主题
     * 贴子主题: [原创]XSLT实现SRT电影字幕修正 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     Qr 帅哥哟,离线,有人找我吗?
      
      
      威望:9
      等级:博士二年级(版主)
      文章:4392
      积分:29981
      门派:XML.ORG.CN
      注册:2004/5/15

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给Qr发送一个短消息 把Qr加入好友 查看Qr的个人资料 搜索Qr在『 XSL/XSLT/XSL-FO/CSS 』的所有贴子 访问Qr的主页 引用回复这个贴子 回复这个贴子 查看Qr的博客楼主
    发贴心情 [原创]XSLT实现SRT电影字幕修正

    XSLT实现SRT电影字幕修正

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn

    喜欢BT电影的朋友经常会遇到这样的问题,在看电影的过程中,由于字幕的制作出现各种各样的错误,令人非常扫兴,最常见的是时间轴不匹配,不是提前就是延后,有时后面一条字幕包括时间截被当作当前字幕来输出。要修正这些错误,只有到网上搜索相关工具来调整和修复。在这里,让我们用XSLT去实现看似不可能完成的任务吧——XSLT实现SRT电影字幕修正!

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn

    首先我们先来看一段截取自电影《玩转21点》的字幕:
    ***************************************
    1
    00:00:08,480 --> 00:00:19,770
    玩转21点

    2
    00:00:22,910 --> 00:00:31,900
    21.REPACK

    3
    00:00:40,107 --> 00:00:42,632
    Winner,winner,chicken dinner

    4
    00:00:43,911 --> 00:00:47,403
    这句话整晚都在我的耳边盘旋

    5
    00:00:48,448 --> 00:00:50,780
    我指的是那个词
    拉斯维加斯赌场的习惯用语

    6
    00:00:51,585 --> 00:00:54,986
    随便问一个以前的赌场老板
    他们肯定知道

    7
    00:00:58,225 --> 00:01:02,662
    这个习语的形成最早要归功于
    一个在Binion赌场的中国商人

    8
    00:01:03,430 --> 00:01:06,399
    他每赢一局的时候都要喊出那句话

    9
    00:01:06,500 --> 00:01:09,958
    那已经是40年前的事了
    可这个口头禅依然被使用

    10
    00:01:10,070 --> 00:01:12,061
    Winner, winner, chicken dinner
    这就是它的由来

    ***************************************
    请注意,影片上出现的每行字幕,实际上格式为:
    ***************
    序号(换行)
    时间轴(换行)
    字幕内容(换行)
    空白行
    ***************
    因为格式固定,这就为我们通过XSLT实现SRT电影字幕修正奠定了算法的基础。正因为空白行的存在,我们可以通过它来作为XSLT处理每行字幕的依据。

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn

    那么,到底怎么实现XSLT修正SRT电影字幕呢?
    首先还得程序语言帮忙,就是将SRT文件转换成规范的XML文档(至于转换方法,不同的程序语言方法不同,我就不一一介绍了,大家如果不会,可以自己去查资料)。转换后我们会得到这样一个XML文档:
    <?xml version="1.0" encoding="gb2312"?>
    <!--encoding根据需要字义,这里我全部使用gb2312-->
    <root>
    SRT文件内容
    </root>

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn

    SRT文件经过转换可以得到符合XML规范的文档,这样我们就可以通过XSLT去处理了,下面是XSL代码,纯粹是XSL命名模板和XPath字符串函数的应用,没有太多注释:
    <?xml version="1.0" encoding="gb2312"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" version="1.0" encoding="gb2312" indent="yes"/>
    <!--srt实际上是文本文件,故取method="text"-->

    <xsl:variable name="ms" select="-490"/>
    <!--ms以毫秒为单位,1s=1000ms-->

    <xsl:variable name="br" select="' '"/>

    <xsl:template match="/">
            <!--SRT末尾要保证有3个换行,否则取不到最末字幕。解决:在将文件内容读入DOM前通过程序语言添加3个换行符。或再加一个递归,条件是:string-length($msg) &gt; 1 and not(contains($msg,' '))-->
            <xsl:call-template name="msg">
                    <xsl:with-param name="msg" select="." />
                    <xsl:with-param name="pos" select="1" />
            </xsl:call-template>
    </xsl:template>

    <!--命名模板msg主要是通过两个换行符“ ”为标记截取每行字幕-->
    <xsl:template name="msg">
            <xsl:param name="msg"/>
            <xsl:param name="pos"/>
            <xsl:variable name="sb" select="substring-before($msg,' ')"/>

            <!--以下两个xsl:if不能解决字幕间隔2n+1个换行符的问题(正常应该是2个换行符)-->
            <!--xsl:if test="string-length($sb) &gt; 1">
                    <xsl:call-template name="msg1">
                            <xsl:with-param name="msg1" select="$sb" />
                            <xsl:with-param name="pos1" select="1" />
                    </xsl:call-template>
            </xsl:if>

            <xsl:if test="contains($msg,' ')">
                    <xsl:call-template name="msg">
                            <xsl:with-param name="msg" select="substring-after($msg,' ')" />
                    </xsl:call-template>
            </xsl:if-->

            <!--通过xsl:choose来能解决字幕间隔2n+1个换行符的问题(正常应该是2个换行符)-->
            <xsl:choose>
            <xsl:when test="starts-with($sb,' ')"><!--递归,跳过2n+1个换行符-->
                    <xsl:call-template name="msg">
                            <xsl:with-param name="msg" select="substring-after($msg,' ')" />
                            <xsl:with-param name="pos" select="$pos" />
                    </xsl:call-template>
            </xsl:when>

            <xsl:when test="string-length($sb) &gt; 1">
                    <xsl:call-template name="msg1">
                            <xsl:with-param name="msg1" select="$sb" />
                            <xsl:with-param name="pos1" select="1" />
                            <xsl:with-param name="pos" select="$pos" />
                    </xsl:call-template>

                    <xsl:if test="contains($msg,' ')">
                            <!--递归,处理下一行字幕-->
                            <xsl:call-template name="msg">
                                    <xsl:with-param name="msg" select="substring-after($msg,' ')" />
                                    <xsl:with-param name="pos" select="$pos+1" />
                            </xsl:call-template>
                    </xsl:if>
            </xsl:when>
            </xsl:choose>

    </xsl:template>

    <!--命名模板msg1是将每行字幕通过 和pos1定位分割序号、时间轴和字幕文字-->
    <xsl:template name="msg1">
            <xsl:param name="msg1"/>
            <xsl:param name="pos1"/>
            <xsl:param name="pos"/>
            <xsl:variable name="sb1">
                    <xsl:choose>
                            <xsl:when test="$pos1 &lt; 3">
                                    <xsl:value-of select="substring-before($msg1,' ')" />
                            </xsl:when>
                            <xsl:otherwise>
                                    <xsl:value-of select="$msg1" />
                            </xsl:otherwise>
                    </xsl:choose>
            </xsl:variable>

            <xsl:choose>
                    <xsl:when test="$pos1=1">
                            <xsl:value-of select="$sb1"/><!--序号--><xsl:value-of select="$br"/>
                            <xsl:if test="$sb1 != $pos">
                                    <!--命名模板msg2处理两条字幕间没有空白行的问题-->
                                    <!--xsl:call-template name="msg2">
                                            <xsl:with-param name="msg2" select="substring-after($msg2,concat($pos,' '))" />
                                            <xsl:with-param name="pos2" select="pos2+1" />
                                            <xsl:with-param name="pos" select="$pos+1" />
                                    </xsl:call-template-->
                                    <xsl:message terminate="yes">
                                            <xsl:value-of select="'message:ERROR'"/>
                                    </xsl:message>
                                    <!--正常情况下遇到xsl:message代码应该停止执行,但实际运行时这里出现两次错误信息,估计某个地方处理还有些问题-->
                            </xsl:if>
                    </xsl:when>
                    <xsl:when test="$pos1=2">
                            <xsl:call-template name="timefix">
                                    <xsl:with-param name="time" select="substring-before($sb1,' ')" />
                                    <xsl:with-param name="pos" select="$pos" />
                            </xsl:call-template>        
                            <xsl:value-of select="' --&gt; '"/><!--箭头-->
                            <xsl:value-of select="substring-after($sb1,'&gt; ')"/>

                            <xsl:value-of select="$br"/>
                    </xsl:when>
                    <xsl:otherwise>
                            <xsl:value-of select="$sb1"/><xsl:value-of select="concat($br,$br)"/>
                    </xsl:otherwise>
            </xsl:choose>

            <xsl:if test="$pos1 &lt; 3">
                    <xsl:call-template name="msg1"><!--递归,继续定位分割当前行字幕-->
                            <xsl:with-param name="msg1" select="substring-after($msg1,' ')" />
                            <xsl:with-param name="pos1" select="$pos1+1" />
                            <xsl:with-param name="pos" select="$pos" />
                    </xsl:call-template>
            </xsl:if>

    </xsl:template>

    <!--命名模板timefix,处理时间轴-->
    <xsl:template name="timefix">
            <xsl:param name="time"/>
            <xsl:param name="pos"/>

            <xsl:variable name="h" select="substring($time,1,2)"/>
            <xsl:variable name="m" select="substring($time,4,2)"/>
            <xsl:variable name="s" select="substring($time,7,2)"/>
            <xsl:variable name="u" select="substring($time,10)"/>

            <xsl:variable name="u1"><!--求时间轴的毫秒部分与修正数值的和-->
                    <xsl:choose>
                            <xsl:when test="number($u) &lt; $ms*-1">
                                    <xsl:value-of select="format-number(((number($u)+$ms) mod 1000)+1000,'000')"/>
                            </xsl:when>
                            <xsl:otherwise>
                                    <xsl:value-of select="format-number((number($u)+$ms) mod 1000,'000')"/>
                            </xsl:otherwise>
                    </xsl:choose>
            </xsl:variable>

            <xsl:variable name="uj"><!--求毫秒进位-->
                    <xsl:choose>
                            <xsl:when test="(number($u)+$ms &gt; -1) and (number($u)+$ms &gt; 1000)">
                                    <xsl:value-of select="floor((number($u)+$ms) div 1000)"/>
                            </xsl:when>
                            <xsl:when test="number($u)+$ms &lt; 0">
                                    <xsl:value-of select="floor((number($u)+$ms) div 1000)"/>
                            </xsl:when>
                            <xsl:otherwise>0</xsl:otherwise>
                    </xsl:choose>
            </xsl:variable>

            <xsl:variable name="s1" select="format-number((number($s)+$uj) mod 60,'00')"/><!--求秒-->
            <xsl:variable name="sj"><!--求秒进位-->
                    <xsl:choose>
                            <xsl:when test="number($s)+$uj &gt; 60">
                                    <xsl:value-of select="floor((number($s)+$uj) div 60)"/>
                            </xsl:when>
                            <xsl:otherwise>0</xsl:otherwise>
                    </xsl:choose>
            </xsl:variable>

            <xsl:variable name="m1" select="format-number((number($m)+$sj) mod 60,'00')"/><!--求分-->
            <xsl:variable name="mj"><!--求分进位-->
                    <xsl:choose>
                            <xsl:when test="number($m)+$sj &gt; 60">
                                    <xsl:value-of select="floor((number($m)+$sj) div 60)"/>
                            </xsl:when>
                            <xsl:otherwise>0</xsl:otherwise>
                    </xsl:choose>
            </xsl:variable>

            <xsl:variable name="h1" select="format-number((number($h)+$mj) mod 24,'00')"/><!--求小时-->

            <xsl:value-of select="concat($h1,':',$m1,':',$s1,',',$u1)"/>

    </xsl:template>

    </xsl:stylesheet>

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn

    前面的注释已经提到,srt实际上是文本文件,故xsl:output设定method="text",当程序语言通过XSL转换XML为文本格式的字符串并输出(这个也不是本文的重点,大家可以上网搜)后,将其后缀名定“.srt”就是我们修正后的字幕文件了。

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn

    当然,细心的朋友会注意到代码中曾经定义了一个变量pos,但并没有发现其在代码中的作用。其实,它是为msg2定义的,目的是处理两条字幕间没有空白行的问题,但这个命名模板我并没有完成它,因为这样做并不是最好的,而且,最近一直在用SVG进行开发,时间上安排不过来。

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn

    下面是我给出的XSLT修正SRT电影字幕另一种思路:命名模板msg不要以两个换行符“ ”为标记截取字幕,而是以“序号+换行符( )”为标记截取字幕,时间轴的处理同上。这样写代码会方便很多。

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn

    另外,本文的思路和代码基本上是解决前面提到的两个问题,其实还能做得更多些,我这里仅仅个初步,感兴趣的朋友可以把它完善得更好。

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn

    最后,要提的是<xsl:message terminate="yes|no"/>,当XSL解析XML出错时,可以用它来返回错误信息,程序语言同样可以接受这个错误信息的返回,这样可以提高程序的交互性能,但不同的程序语言,错误信息的捕捉不尽相同,大家可以根据实际情况查阅相关帮助文档。

    原创:Qr,8/17/2008 http://Qr.blogger.org.cn


    [此贴子已经被作者于2008-8-17 23:16:23编辑过]

       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    没人帮忙,那就靠自己,自己才是最好的老师!本人拒绝回答通过站内短消息提出的问题!

    blog:http://Qr.blogger.org.cn

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/8/17 20:50:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 XSL/XSLT/XSL-FO/CSS 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/4/28 10:59:23

    本主题贴数2,分页: [1]

     *树形目录 (最近20个回帖) 顶端 
    主题:  [原创]XSLT实现SRT电影字幕修正(12352字) - Qr,2008年8月17日
        回复:  呵呵,QR的帖子要顶(18字) - ioyichen,2008年8月18日

    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    4,003.906ms