1、使用Python将普通词典文件转化为 XML简介许多完善的软件项目已经使用普通文本配置和资源文件很多年了,但没有出现什么重大问题。随着项目的 扩展和复杂性的增加,对更高严谨性和更强适应性的需求也随之增加。通过XML以及使用具体标准的 XML应用程序,您可以得到以下获益:跨项目和跨平台兼容性、稳健性,以及在Unicode等领域的可扩 展性。常用缩略词 HTK:隐马尔可夫模型工具包(Hidden Mar kov Model Tooht)PLS:发音词汇规范(Pr onunc iation Lexic on Spec ific ation)XML:可扩展标记语言(extensible Mar kup
2、 Lang uag e)通过将普通文本文件转化为相关的开源标准,您还可以提高灵活性和可靠性。语音识别工作中的词典就是 本文使用的一个很好的例子。不管您的开源项目是否转而使用XML格式的资源文件,您都可以在不损失 功能的情况下在您的工作中运用XML标准。在本文中,我们将学习如何轻松地在普通文本和Pronunciation Lexicon Specification(PLS)格式之间进行 转换。几个示例展示如何存储PLS格式的自定义词典,并将数据提取为所需的普通文件。回页首示例:词典词典是在语音识别工具中使用的单词列表。它们包含的信息涉及到如何打印或用图形显示单词,它如何使 用音素发音。经常与Hi
3、dden Markov Model Toolkit(HTK)一同使用的词典在语音控制项目中得到广泛使 用(参阅参考资料)。清单1是来自一个VoxForgeHTK词典的一段摘录。清单1.清单1来自一个VoxForge HTK词典的一段摘录agency agency ey j h ih n s iyagenda agenda ax j h eh n d axAGENT AGENT ey j h ih n tAGENTS AGENTS ey j h ih n t sager ager ey g erAGES AGES ey j h ih z如果复制和粘贴本文代码,添加一个制表分隔符建议您直接从源头获
4、取词典。本文用HTML显示,HTML用空格替换制表分隔符。如果您从本文执行复制和粘贴操作,需要用一个制表分隔符替换多个插入的空格(t),否则脚本运行会失败。清单1中的文件包含三个以制表符分隔的字段:一般描述单词的标签 当您希望打印或在屏幕上显示一个单词时围绕单词的方括号(完麦)来自Arpabet集(参阅参考资料)的一系列单一空格分隔的音素,描述单词的读音在上述示例中使用英语发音,多半包含在 American Standard Code for Information Interchange(ASCII)字符中。CMU Sphinx项目以类似的方式(参阅参考资料)将词典(或字典)存储在CMU S
5、phinx上下文中。清 单2给出一段摘录。清单2.来自一个CMU Sphinx词典的一段摘录ag enc y ey jh ah n s iyag enda AH JH EH N D AHag endas AH JH EH N D AH zag ent EY JH AH N Tag ents ey jh ah N T sag er EY JH ER在清单2中只有两个字段:单词/字素及其音素。两个词典示例有一些细微差别:单词和音素是完全不同的类型。音素有一些微妙的差异。对待标点符号(逗号和惊叹号等)的方式稍有不同。您可以在目前下载的PocketSphinx中的cmu07a.dic文件中看到整个字典
6、(参阅参考资料)。由于词典给出特定单词的发音,您可能需要编辑文件以适应特定的人或方言。久而久之就可以在自定义词 典中建立起知识资产了。使用文本编辑器很容易就可以编辑普通文件,但也易于引入错误,比如:使用文 件标准以外的分隔符,插入非ASCII字符,以错误的顺序放置字段,不当地对字段进行排序,在需要的地 方缺少方括号,等等。普通文件还有一点不足之处。在您构建自定义文件时,始终与其他语音项目不兼容。而标准XML格式(比 如PLS)的词典,一旦被两个项目识别到,在两者中都能立即相互兼容。回页首语音词典规范(Pronunciation Lexicon Specification)PLSA有一个简单的基
7、本格式,如清单3所示。清单3.清单3基本PLS格式.phoneme XML描述可包含多个lexeme子元素的root元素lexicon。每个lexeme可以包含多 个grapheme元素和多个phoneme元素。规范允许您覆盖alphabet属性,但不允许您覆 盖xml:lang语言属性。要将词素存储为不同的语言,您非常需要分离PLS词典文件。这个词典中 的默认字母系统是ipa,它是指表示发音的InternaMonal Phonetic Alphabet(IPA)系统(参阅参考资料)。IPA音素表示法是多字节Unicode字符。HTK和Sphinx都使用纯ASCII代码。本文稍后会讨论这个 重
8、要考虑因素。使用PLS规范的优势在于,它添加了更加严谨的结构,能够让您存储更多信息,比如词性和具体字母。词性细节在英语中很重要,因为一些拼写起来相同的单词(同形异义词)发音不同,具体视语法角色而定。例如,perfect在作为形容词和作为动词时发音不同,因为重音在不同的地方。属性中存储的额外信息能够 让您根据需要从整个文件提取特定记录。使用这一方法,您可以在多个phoneme元素中搜索一个特定 的字母。将PLS词典看作是一个词典信息数据库,从中您可以提取与所使用的语音工具相关的详细信息。清单 4是一个PLS格式的示例。清单4.清单4 一个PLS格式的单词ag enc yey j h ih n s
9、 iyEY jh AH N S lY清单4中的示例仅存储一个可能有两个音素表示的单词。您可以使用alphabet属性过滤出其中一 个phoneme字符串。lexeme元素显示noun的role属性。这里面虽然信息量大,但在本例 中是冗余的,因为该单词仅作为一个名词使用,没有复杂的发音情况。通过并排放置两个不同来源的phoneme表示,您己经可以分辨出细微的差别了。该信息在解决语音识 别问题方面会很有帮助。CMU Sphinx和HTK都不能直接使用PLS词典,但是HTK工具包的simon(参阅参考资料)前端可 以。如果您直接使用HTK或Sphinx,一定要确保可以轻松实现普通文件和PLS之间的来
10、回转换,而不 丢失任何信息。以下几节展示如何使用Python实现普通文件与PLS之间的来回转换。假定您在普通词典文件中有自定 义信息。回页首转化为PLS清单5中的代码使用Python,但是您可以通过许多其他方式完成相同的工作。(例如,参阅参考资料中 有关 Extensible Stylesheet Language Transformations(XSLT)的 developerWorks 教程)。有些人希望 使用可在每一小步检查XML稳健性的库,获得更多有关问题出处的即时反馈,特别是当源文件较大且易 于包含错误和不一致性时。下面的示例将检查留到最后一步,这意味着一定的置信水平,表示普通文件状
11、 态良好。清单5.清单5转化为PLSfr om elementtr ee.ElementTr ee impor t par seimpor t str ing as strimpor t sysimpor t c g n#c al1 with#python flat2pls.py vox#or#python flat2pls.py spx#if len(sys.ar g v)=2:sr c=sys.ar g vlelse:exit(wr ong ar g s)#outfi1e=mylex+s r c+.plspr int out is+outfi1eout=open(outfile,w)out
12、.wr ite(n,)#now the 1exemesif sr c=vox:f=open(vf.lex,r)for line in f:line=s tr.s tr ip(line)wor d=s tr.s plitfli n e,t)#g r=str.s tr ip(wor d l,)g r=c g i.esc ape(wor d0)out.wr ite(n n+g r+n+wor d2+n)else:#sr c is sphinxf=open(c mu.dic,r)for line in f:line=s tr.s tr ip(line)wor d=s tr.s plit(lin e,t
13、)g r=c g i.esc ape(wor d0)out.wr i te(n n+g r+n+wor dl+r)#ended lexemesout.wr ite(nn)out.c lose()#now c hec k the output is oktr ee=par sefoutfi1e)lexi c on=tr ee.g etr oot()mylexc ount=0for 1exeme in 1exic on:mylexc ount+=1pr int Found%(number)d 1exemes%number:mylexc ount清单5首先从XML解析库elementtree和一些支
14、持的库导入模块(参阅参考资料)开始。导入 不同版本上的ElementTree会涉及到稍微不同的语法,具体取决于您安装模块的方式。示例代码来自 openSUSE,其中模块是从源头安装的,但Ubuntu可能需要fromxml.etree.ElementTree import parseo 模块 str 支持一些字符串操作,sys 提 供给您对文件的访问权限,且cgi提供处理XML数据所必要的一个非常重要的转义例程。代码打算获 取一个命令行界面(CLI)参数,告诉它是否要从CMU Sphinx格式或HTK/VoxForge转换。然后示例代 码打开输出文件,并编写适合PLS的XML序言。由于在此阶段您
15、不存储任何Unicode字符,所以只需 打开文件仅进行纯ASCII访问就行了。此时,清单5中的代码是:逐行处理源文件,将字段分为独立的字符串,编写lexeme,grapheme和phoneme组 件。标识phoneme,如果传入的数据来自VoxForge词典,那么使用属性 a1phabet=x-htk-voxforge”,如果数据来自 Sphinx,那么使 用 alphabet=nx-cmusphinxno当导入第一个字段时,它可能包含在XML中引起问题的字符,比如连字符(&),除非使用cgi.escape()进行转义。最后是代码:编写关闭标签 关闭PLS文件,然后将其重新加载为一个XML文件
16、 阅读整个文件,计算lexeme元素 报告词素的计数如果报告了计数,那么XML显得可靠且符合语法规则。清单6是来自VoxForge HTK词典的一段结果摘录清单6.清单6 VoxForge HTK词典结果摘录AGENDAax j h eh n d axAGENTey j h ih n t回页首从PLS转换您需要知道的一点是,您可以轻松实现从PLS格式到普通文件的转换。清单7中的代码假定您有存储为 PLS格式文件的词典,而且您的语音识别项目只能使用HTK或CMU Sphinx格式的普通文件。清单7.从PLS转换fr om elementtr ee.ElementTr ee impor t par
17、 se impor t str ing as str impor t sys#c all with#python pls 2flat.py x-htk-voxfor g e mylexic on#or#python pls2flat.py x-c musphinx mylexic on.dic#if len(sys.ar g v)1:alpha=sys.ar g vl#if alpha=x-htk-voxfor g e:tr ee=par se(mylexvox.pls)else:tr ee=par se(mylexspx.pls)lexi c on=tr ee.g etr oot()for
18、lexeme in lexic on:for c hild in 1exeme:#pr i nt c hi Id.tagif c hild.tag-8:=g r apheme:if alpha=x-htk-voxfor g e:g r=str.upper(c hi Id.text)pr int g r,t,+g r+,t,else:g r=c hild.text pr int g r,t,if c hild.tag-7:=phoneme:if c hild.g et(alphabet)=alpha:pr int c hild.text这一段简短的脚本使用elementtree库来解析PLS X
19、ML文件。它建立root元素,然后迭代子词 素,寻找grapheme和phoneme,并将值写到相关格式的文本文件中。脚本请求标签中的最后8个 字符,即grapheme,因为标签会有一个命名空间前缀返回。它为HTK重新创建三个字段,为CMU Sphinx创建两个字段。回页首合并和处理Unicode清单8中的脚本使用两个PLS文件来创建一个常见的PLS文件,其中包含两个原始文件的信息。它还 将VoxForge phoneme字符串转换为Unicode并将Unicode版本存储在用alphabet=nipan属性标识的一个独立的phoneme元素中。清单8.清单8合并Unicode#!/usr/b
20、in/python-u#c odi ng:utf-8#c halleng e is to mer g e two pls files#g iven two pls files,mer g e them into one#impor t elementtr ee.ElementTr ee as ETfr om elementtr ee.ElementTr ee impor t par se impor t str ing as str impor t c odec s impor t c g i#tr eevox=ET.par se(my1 exvox.pls)tr eespx=ET.par s
21、e(mylexspx.pls)#lexvox=tr eevox.g etr oot()1exspx=tr eespx.g etr oot()#phons=aa:u a,ae:uae,ah:ua,ao:uo,ar :uer,aw:uau,ax:ue,ay:uai,b:ub,c h:utJ,d:ud,dh:u5,eh:ue,el:uol,en:uan,er:u*9r,ey:uei,f:uf,mlum.n iun.n g iuo.owiuoo.oy r uoi.pzup.r r ur.s us,w:uw,y:uj,z:uz,zh:u3,s il:#def to_utf(s):myp=s tr.s p
22、lit(s)myi pa=for p in myp:myi pa.append(phonsp)r etur n str.j oin(myipa,)#outfi1e=my21exmr g.pls out=c odec s.openfoutfile,enc oding=utf-8,mode=w)#out.wr ite(n)#sc an the two pls,c r eate dic tionar y voxdic t=for 1exeme in 1exvox:g r=str.Iower(lexeme0.text)ph=1exeme1.text voxdic t g r =ph,#for 1exe
23、me in 1exspx:g r=1exeme0.textph=1exeme1.textif g r in voxdic t:voxdic tg r.add(ph)else:voxdic tg r =ph,#for g r in sor ted(voxdic t.iter keys():out.wr ite(n n+c g i.esc ape(g r)+)#pr int%s:%s%(key,voxdic tkey)for ph in sor ted(voxdic tg r):al ph=x-htk-voxfor g e i f ph.i slower()else x-c musphinx ou
24、t.wr ite(n+ph+)if ph.1 slower():phi pa=to_utf(ph)out.wr ite(un+phipa+)out.wr ite(n)#done,c lose filesout.wr ite(n)out.c lose()#now c hec k the output is oktr ee=par se(outfile)1 exi c on=tr ee.g etr ootOmylexc ount=0for lexeme in lexic on:mylexc ount+=1pr int Found%(number)d 1exemes%number:mylexc ou
25、nt首先以hashbang(#!)表达式开始,后接标识Python解释器的一个特殊指示符。在第二行上,该代码包 含Unicode字符。然后脚本导入大量模块,elementtree,codecs和cgi,这些模块在 处理Unicode时很有用。您告诉解释器两个PLS文件在哪里,并指向其root元素。phons变量存储一个特殊的字典,其中包含从CMU Arpabet代码到等价的Unicode组合的一个映射。该字典将现有的phoneme字符串转换为一个Unicode版本。您可以根据自己的需求随意修改映射,例如,您可能觉得Unicode中aa 的等价词是uct:这拉长了 a的发音。一个定义好的函数()
26、将ASCII Arpabet字符串转化为Unicode。基础工作的最后一部分是打 开一个文件来存储输出,确保该文件知道它要准备好接受Unicode,并将PLS序言写到其中。现在准备处理文件,方式就是创建两个特殊的内部Python字典,一个是各个PLS文件,使用elementtree库扫描它们。假设grapheme将是第一个子元素,而phoneme是词素的第 二个子元素。脚本将来自第一个文件的所有记录添加到新的合并字典。在扫描第二个文件时,如果键己经 存在于新的合并字典中,您可以添加其音素集。如果不存在,在合并的字典中创建一个新的键项。在循环 结束时,新的合并字典同时包含来自原始文件和相关的一个
27、或两个phoneme字符串的键。从刚才创建的合并编写新的PLS文件。在扫描整个字典时,添加alphabet属性来区分两个phoneme。写出现有的音素之后,创建一个新的phoneme字符串,它是CMU Arpabet字符串 的Unicode等价字符串,您可以根据您的需要从HTK或Sphinx版本(或两者)获取它。最后,结束root元素,关闭文件,并再次解析它,就像之前检查它符合语法规则一样。结果类似于清单9。清单9.清单9合并结果lexemeag endaAH JH EH N D AHax j h eh n d axsd3ends(g r aphemeag endasAH JH EH N D
28、AH Zag entEY JH ah n Tey j h ih n teid3ir)tag entsEY JH ah N T s有了合并的PLS字典,您可以应用Extensible Stylesheet Language(XSL)或其他任何程序来生成需要 的结果,不管是普通文件还是新的特定PLS文件。在理论上,您也可以在该文件中存储其他phoneme字符串。但是,这是对PLS规范的一个非标准使用。Kai Schott已经做了大量PLS相关工作,而且己经准备好了几个不同语言版本的文件以供下载,特别是 德语版本(参阅参考资料)。回页首未解决的问题尽管您可以从普通文件获得大量信息,以下问题仍然未得到
29、解决。从多个字素中选择有时您需要处理同一语言中一个单词的多个拼写。对于两个拼写法,角色和音素是相同的,因此 在同一个词素中有多个字素。但是,在PLS中您无法添加一个属性到grapheme元素,就 像phoneme元素的alphabet属性。缩略词词典常常包含缩略词。PLS在一个名为(alias的工exeme的子元素中处理这些。要从 一个普通文件自动构建一个PLS,您需要一种方式来将缩略词与词典中真正的单词区分开来。普 通文件不一定有这个信息。角色/词性与缩略词一样,普通文件中不提供词性信息来将role属性构建到PLS中。回页首结束语在本文中,您了解了如何非常简单地进行普通文件和PLS格式文件之间的转换。存储PLS格式的词典 有潜在的优势。开源项目可能使用、也可能没有使用XML格式的资源文件。这是项目经理要做出的决策。他们自己就可以评估资源,并且以他们在社区中普遍接受的最佳方式应用它们。同时,您可以在自己的工作中运用XML标准,而不损失任何功能。对于在语音识别工作中使用的词典和 字典,您可以通过对PLS格式的自定义词典评分来增加跨项目能力、可靠性和实用性。您可以轻松按需 提取数据到所需的普通文件。