查看原文
其他

巧用正则表达式整理双语术语数据

韩林涛 简言 2022-07-09

今天下午在一个微信群中看到这样一篇文章:《Glossary | 文字學術語中英文對照表》。


大部分人看到这个文章后肯定不知所云,只有真正需要这些双语术语的专家才会如获至宝。但这些专家的时间是宝贵的,他们更加需要的是一个在阅读文献或翻译文献时立即可以查询的术语库,而不是一篇长长的中英混杂的文章。


这篇文章让我想起很早以前在语智云帆实习时做的一个小玩意儿,当时叫做: LingoSail Online Converter,长这个样子:


基本功能就是:输入一段中英文的术语数据,可以根据需要在中英文之间加空格、制表符,或者把中英文掉个顺序。


为什么要做这样的处理呢?因为我想把网页上找到的双语术语存到Excel表格中,再存到我的术语库中,如果中英文不分开,就没办法保证双语术语分别存在不同的单元格中。


一、使用 LingoSail Online Converter 尝试处理上述文章中的术语


我想试试能不能把刚才那篇文章中的双语术语处理到 Excel 表格中,于是复制原文,粘贴到 LingoSail Online Converter 中,试试在中英文之间加个制表符(Tab),然后再粘贴到 Excel 表格中存下来。


我是这样操作的:


第一步:先把所有内容拷贝到记事本中,将中英文之间的中文逗号全部替换掉,变成如下模样(注:原文中有两种类型的逗号,一种是英文原文中所有的英文逗号,一种是中英文之间的中文逗号):



第二步:把上面这部分内容粘贴到 LingoSail Online Converter 中,点击“中英文间加空格”,变成如下模样:


第三步:经过第二步之后,下面的文本框中英文和中文之间就多了空格,我现在把下面文本框中的内容移到上面的文本框,然后点击“英中文间空格转制表符”按钮,变成如下模样:


第四步:上一步看起来没有什么变化,因为空格符和制表符在这里都不可见,肉眼看到的都是空白,不过将下面文本框中的内容粘贴到 Excel 表格中就能看出效果了:



假如不做这样的处理,我们粘贴到 Excel 表格中就是这样的:



转换前后的区别还是很明显的。


二、上述步骤的问题


虽然使用上述步骤我最后达到了效果,但是仔细一看也有问题:


  1. 第一步对中文逗号的替换操作显得有点 low

  2. Excel 本身就支持使用逗号来切割导入的文本,如下,根本用不着上面这个工具,不过从导入TXT文本到设置文本分割方法也需要好几步:




确实,使用上面的几个步骤来处理这段文本有点画蛇添足,但是本文想展示的一种使用正则表达式来实现上述步骤的原理。比如:如果有人给了1万块,让我给 LingoSail Online Converter 添加一个按钮,可以一键处理中英文之间带有逗号的双语术语,将其转换成 Excel 表格,我应该怎么做?效果图如下:


三、上述步骤的技术实现


在看下面这段文字之前,可以先回顾我之前的文章:《小议网站本地化中的正则表达式》。


我们先看“中英文间加空格”这个功能如何实现的。


这个功能的逻辑比较简单:


在上面的文本框中输入一段文字;定位哪些是中文、哪些是英文;在中文和英文之间加个空格;再下面的文本框中呈现处理完成后的文字。


在实际功能开发时要用到以下关键代码:


<script type="text/javascript">

function AddSpace(inText, outText)

{

var outT;

outT = inText.value.replace(/([\u4E00-\u9FA3])([A-Za-z0-9\(\[\{@#])/g,'$1 $2');

outT = outT.replace(/([A-Za-z0-9\.,!@#?\)\]\}])([\u4E00-\u9FA3])/g,'$1 $2');

outT = outT.replace(/([。,!?〔〕()《》“”]) +/g,'$1');

outT = outT.replace(/ +([。,!?〔〕()《》“”])/g,'$1');

outT = outT.replace(/ +/g,' ');

outText.value=outT;

}

</script>


下面我说一下这段代码的含义(前方高能,做好心理准备)


代码一:


<script type="text/javascript">...........................</script>


这一段代码就跟警察穿的警服、医生穿的白大褂一样,起代码类型识别功能。像这样的一段代码,浏览器读取之后就立刻知道这是一段“JavaScript”代码,简称“JS”。


那么“JavaScript”代码在这里起什么用呢?作用就是:为网页中的某个按钮赋予魔力。只要一点击某个按钮,就会运行这段代码中的功能,达到你预期的目的。


那么代码中的功能又是什么呢?


在“JavaScript”代码中,第一行是:function AddSpace(inText, outText)


“function”的中文是“函数”,但我更愿意称之为“功能”。我们在这里定义的功能叫做“AddSpace”,这是我随便取的功能名称,我也可以叫它“AddTMDSpace”,也可以叫它“TMDAddSpace”,都可以。


“AddSpace”这个名字后面有一个括号,括号中有一个“inText”和一个“outText”。这也是我自己取的名字。我想把待处理的文字放到“inText”这个东西里,处理完之后的文字放到“outText”里头。


这两个“东西”的学名叫做“Variable”,中文译为“变量”。它的拉丁语词源是“variare” ,意为“to change”。“Variable”这个词的中文译得很好,“变化的量”,英文解释是“Having no fixed quantitative value”。


学生在给老师交作业时,可以在附件中添加一个“我的作业.docx”文件。至于文件中是不是老师想要的作业,那可不好说,万一文件中了病毒,打不开呢?但名字始终是“我的作业.docx”,这个文件就是“Variable”,变量名叫做“我的作业.docx”。


我们刚刚起了两个变量名,分别是“inText”和“OutText”,但怎么使用它们还不知道。


接下来我们看后面的花括号里的内容。在花括号的第一行,我们看到的是非常简短的一段代码:


var outT;


“var”可以理解为“Variable”的缩写,但在这里你得把它看成是个“动词”,这个动作执行之后的结果是,变出来一个新的变量“outT”。现在这个变量里什么都没有呢,我们看一行发生了什么。


outT = inText.value.replace(/([\u4E00-\u9FA3])([A-Za-z0-9\(\[\{@#])/g,'$1 $2');


这真是好长的一段代码,我们先看括号里的东西:


/([\u4E00-\u9FA3])([A-Za-z0-9\(\[\{@#])/g


在我们之前的帖子里,我介绍过这部分:[A-Za-z0-9],这代表的是所有英文大小写字母和所有的数字。

“\(”:“\”是转义符,这个符号代表的是左括号

“\[”:这个符号代表的是左大括号

“\{”:这个符号代表的是左花括号

“@”:这个符号代表的就是@

“#”:这个符号代表的就是#


有了这个表达式,我们希望可以找到大部分的英文和英文标点符号。我们用圆括号将这个表达式扩在一起,当成一个整体:


([A-Za-z0-9\(\[\{@#])


接下来再看前面的“整体”:([\u4E00-\u9FA3])


这个解释起背景来比较复杂,我就先跳过了,简单来说,这个符号代表的就是任意的汉字。


那么([\u4E00-\u9FA3])([A-Za-z0-9\(\[\{@#])合在一起能匹配什么玩意儿呢?


大家看个例子:


看到了吧,这个正则表达式寻找到了所有挨在一起的“中文+英文”和“中文+英文标点符号”。


不过这个表达式还没有写完,我们在前面还加了一个“/”,在后面加了一个“/g”。


用在这里,这两符号的作用可以理解为:Please find ALL these matches. “g”是“Global”的意思。简而言之,有了这段正则表达式可以找到要处理的双语材料几乎所有挨在一起的中文和英文。


代码三:


我们先不急着把所有内容都看完,先把跟正则表达式相关的内容分析完。下面一行是这样的:


outT = outT.replace(/([A-Za-z0-9\.,!@#?\)\]\}])([\u4E00-\u9FA3])/g,'$1 $2');


这里面的代码跟之前的很像,比如:“([A-Za-z0-9\.,!@#?\)\]\}])”一看就是用来匹配大小写英文字母数字和标点符号。之所以这里的标点符号比上面的多,是因为在英文和中文混杂的文本里,中文前容易出现各种类型的标点符号,比如叹号、问号、句号、右括号、右中括号、右花括号等,所以会比上一行的代码多不少标点符号。而匹配中文的代码并没有什么变化。


这样一来,最长最复杂的两行代码我们已经知道它要匹配什么内容了,那么匹配之后干什么呢?


outT = inText.value.replace(/([\u4E00-\u9FA3])([A-Za-z0-9\(\[\{@#])/g,'$1 $2');

outT = outT.replace(/([A-Za-z0-9\.,!@#?\)\]\}])([\u4E00-\u9FA3])/g,'$1 $2');


这里我们会看到两行代码有个共同点,都有一个“replace”,即“替换”。所以能够猜出来这个代码的功能是完成替换操作。怎么替换的呢?


比如在这个代码中:outT = inText.value.replace(/([\u4E00-\u9FA3])([A-Za-z0-9\(\[\{@#])/g,'$1 $2');


我们先用“/([\u4E00-\u9FA3])([A-Za-z0-9\(\[\{@#])/g”这个正则表达式找到所有的中文和英文,然后把他们分开,变成这样:(中文)(英文),然后用后面的“$1”和“$2”分别代表前面的(中文)和(英文)。然后再在他们中间加一个空格,变成“$1 $2”,这样一来我们就可以用“,”后面中间加了空格的中文和英文,去替换“,”前匹配出来的没有加空格的中文和英文,完成了替换操作。


大家可能需要多思考一段时间才更好理解这个替换操作。


待处理的文本最开始是放在“inText”这个变量中,当空格加到待处理的文本之后,我们实际上是对“inText”这个变量名所对应的变量值(value)进行了处理,我们接下来要把处理之后的内容放到我们用“var outT;”这段代码生成的空的变量“outT”中。所以代码才写成:


outT = inText.value.replace(/([\u4E00-\u9FA3])([A-Za-z0-9\(\[\{@#])/g,'$1 $2');


现在所有找到的中英文之间都加了空格了。我们再去给所有的英中文之间加空格,于是有了下面的代码:


outT = outT.replace(/([A-Za-z0-9\.,!@#?\)\]\}])([\u4E00-\u9FA3])/g,'$1 $2');


我们将“outT”这个变量自身包含的待处理的文本进行了加空格处理,然后再去更新“outT”包含的内容。


通过这两步操作,无论是中英文还是英中文,基本上都可以加上空格了。


代码四:


我们继续看后面的三行代码:


outT = outT.replace(/([。,!?〔〕()《》“”]) +/g,'$1');

outT = outT.replace(/ +([。,!?〔〕()《》“”])/g,'$1');

outT = outT.replace(/ +/g,' ');


这三行代码感觉都跟标点符号有关系。比如第一行中的正则表达式:([。,!?〔〕()《》“”]) +


不知道大家是否还记得正则表达式中“+”的作用,它表示“匹配前面的子表达式一次或多次”,那么这个代码中加号前面的是什么呢?是“([。,!?〔〕()《》“”]) ”吗?


其实不是,如果仔细观察会发现,加号前面是个空格。也就是说这段代码寻找的是各种标点符号后面有空格的情况。示例如下:



那么后面的“$1”指代的是哪段代码呢?是“/([。,!?〔〕()《》“”]) +/g”吗?


事实上,“$1”对应的是空格前面所有圆括号里的内容,即其中任意的中文标点。所以这段替换代码的实际意义是,在前面两行代码完成了中英文之间或英中文间添加空格的操作后,把中文标点符号后面的空格都去掉,因为在中文中,我们不在标点符号后面加空格。


下面的那段代码也好理解了,因为我们在中文中不在标点符号前面加空格,所以要把中文标点符号前面的空格去掉。


“/ +([。,!?〔〕()《》“”])/g”这段代码中加号前面是一个空格,空格和加号放在一起用于匹配一个或多个连续的空格。


最后,我们再来看最后一行代码:


outT = outT.replace(/ +/g,' ');


这个代码中“/ /g”中间只有一个空格和一个加号,表示要去寻找所有的连续的多个空格,而逗号后面的单引号中看起来什么都没有,但实际上有一个空格,因此这段代码的作用是找到所有的连续多个空格,把他们变成一个空格。


怎么样,现在大家知道“中英文间加空格”这个功能是怎么实现的了吧?我们不仅给中英文间加空格,也要给英中文间加空格,还要注意多加的空格要删掉,不该加的空格不加。


四、空格如何变成制表符


我们一般将表格称为“table”,所以大家可以猜一下我们键盘上的“Tab”键为什么名字叫“Tab”,为什么我们会称它为“退格键”,为什么按了它之后有时会产生“制表符”或“制表位”。大家再猜猜“tabular”是什么意思?再想想著名的数据可视化公司“Tableau”跟这几个单词又有什么关系?


这个文字游戏大家可以自己去研究,我们接下来介绍一下如何用代码将所有的空格转成制表符,方便我们把文字粘贴到Excel表格中保存。


替换代码如下:


outT = inText.value.replace(/([A-Za-z0-9\(\[\{@#]) +([\u4E00-\u9FA3])/g,'$1\t$2');


怎么样,是不是很简单粗暴?找到所有的中间有空格的英文和中文组合,用“\t”符号将所有的空格替换。这个“\t”代表的就是制表符。


五、如何将中英文间有逗号的文本直接转变成可以粘贴到Excel表格中的文本


这个就是我们最初遇到的那个问题,现在想想似乎并不难了。我们只需要匹配所有中间有逗号的英文和中文,然后把中间的逗号用“\t”符号替换即可,如下:


outT = inText.value.replace(/([A-Za-z0-9\(\[\{@#]),([\u4E00-\u9FA3])/g,'$1\t$2');


是的,就是这么简单粗暴,在上面代码基础上把之前匹配空格的代码替换成一个中文的逗号即可。


 结语


为了能大家不用写代码就能感受到上述正则表达式的功能,我将之前的 Lingosail Online Converter 换了一个名字,叫做“BiTerm”,并把网页迁移到了translation.education这个网站上,大家访问:http://translation.education/biterm 即可使用上面的正则表达式来处理双语术语数据。


当然,也可以点击“阅读原文”,直接访问。


希望这些简单的代码能够帮助更多人节省时间,制作出更多的可方便查询的术语库。


最后,再次感谢语智云帆的小伙伴们,在那里实习的三年让我受益匪浅。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存