通过替换现有文本中的单词进行翻译
在处理文本时,如何翻译某些单词(或表达)并且保持文本的完整性(包括标点符号等)是一个常见的问题?
翻译的内容来自一个查找表,里面包含了单词、短语和一些表情符号,比如L33t、CUL8R、:-)等。
简单的字符串搜索和替换方法是不够的,因为这样可能会替换掉更长单词的一部分(比如把“cat”替换成“dog”,但“caterpillar”就变成了“dogerpillar”)。
假设输入是这样的:
s = "dogbert, started a dilbert dilbertion proces cat-bert :-)"
翻译后,我应该得到类似这样的结果:
result = "anna, 开始了一个 george 的讨论 过程 cat-bert smiley"
我不能简单地进行分词,因为这样会丢失标点符号和单词位置。
正则表达式可以处理普通单词,但对像表情符号:-)这样的特殊表达式就不太管用了。
re.sub(r'\bword\b','translation',s) ==> translation
re.sub(r'\b:-\)\b','smiley',s) ==> :-)
目前我在使用上面提到的正则表达式,并对非字母数字的单词进行简单替换,但这远远不是万无一失的。
(顺便说一下,我使用的是Python)
4 个回答
如果你想要一个不使用正则表达式的方法,这里有个主意。以下是我会采取的步骤。
准备工作:
- 创建一个字典,把需要替换的单词和它们的替换词连接起来。
- 建立一个三叉树,用来存放需要替换的单词。
搜索和替换:
- 用split()方法把单词按空格分开。我这里说的单词是指那些没有空格的字母组合。
- 遍历所有的单词:
- 在三叉树中查找这个单词——如果找到部分匹配,检查这个单词的剩余部分是否是标点符号(或者至少不是会导致不匹配的东西)。
- 如果在三叉树中找到了这个单词,就用字典来替换它。
你可以在这里了解三叉搜索树。虽然有现成的三叉搜索树的Python实现,但你也可以很简单地自己做一个。这个方法的主要问题是如果单词前面有标点符号(比如“),但这个问题可以很容易解决。
你这个笑脸的例子在用正则表达式时不管用,原因是 \b 代表的是“单词边界”。因为笑脸里没有“单词”字符,所以就没有单词边界,所以你的表达式匹配不上。你可以用前瞻和后顾来检查是否被空格包围,但要检查标点符号就比较麻烦,因为你的笑脸本身就是由标点符号组成的。
我之前也遇到过类似的问题,就是要把标准的表情符号替换成对应的值。这里有一个表情符号的列表。我把这些表情符号放在一个普通的文本文件里(这样我可以随时添加或删除),用制表符分隔开。
:[ -1
:/ -1
:( -1
:) 1
然后把它读入一个字典里。
emoticons = {}
for line in open('data/emoticons.txt').xreadlines():
symbol, value = line.split('\t')
emoticons[str(symbol)] = int(value)
接着写一个查找函数。
def mark_emoticons(t):
for w, v in emoticons.items():
match = re.search(re.escape(w),t)
if match:
print w, "found "
用这个函数来调用。
mark_emoticons('Hello ladies! How are you? Fantastic :) Look at your man ...')
至于“L33t-speak”(一种网络用语),我有一个单独的文件叫 slangs.txt,内容大概是这样的:
u you
ur you are
uw you are welcome
wb welcome back
wfm works for me
wtf what the fuck
再写一个类似的函数,把这些俚语读入一个字典 slangs{},然后再写一个函数来替换这些俚语。
def mark_slangs(t):
for w, v in slangs.items():
s = r'\b' + w + r'\b'
match = re.search(s,t)
if match:
#print w, "found in:",t, "replacing with",readtable.slangs[w]
t = re.sub(w,slangs[w].rstrip(),t)
...
在Python库中,有一个叫 re.escape() 的函数。
re.escape(string) 这个函数会返回一个字符串,其中所有的非字母数字字符都会被反斜杠转义; 这在你想匹配一个可能包含正则表达式特殊字符的任意字符串时非常有用。
根据你的需求,你可能还想使用 re.findall()。