提取完整单词

9
我有一大批真实世界的文本需要从中提取单词并输入到拼写检查器。我希望能够提取尽可能多的有意义的单词而不会有太多噪音。目前我正在使用 '[a-z]+' 提取所有字母序列。这是一个可以接受的近似值,但它还是会带来很多垃圾内容。
理想情况下,我希望有一个正则表达式(不必漂亮或高效),它可以提取所有由自然单词分隔符(如 [/-_,.: ] 等)分隔的英文字母序列,并忽略任何带有非法边界的英文字母序列。
然而,我也很乐意只能获得所有不与数字相邻的英文字母序列。因此,例如 'pie21' 不会提取 'pie',但 'http://foo.com' 将提取 ['http'、'foo'、'com']。
我尝试过 lookahead 和 lookbehind 断言,但它们是按字符应用的(所以例如 re.findall('(?
更详细地说:数据是邮件数据库,所以它主要是普通英语和正常数字,但偶尔会有像 GIHQ4NWL0S5SCGBDD40ZXE5IDP13TYNEA 和 AC7A21C0 这样的垃圾字符串,我想完全忽略它们。我假设任何包含数字的英文字母序列都是垃圾内容。

最好在正则表达式中使用原始字符串。\d 可以工作,但其他转义序列会出错,而且这可能很难调试。 - Tim Pietzcker
4个回答

18

如果你仅限于使用ASCII字母,则可以使用(设置re.I选项)

\b[a-z]+\b

\b 是一个单词边界的锚点,只匹配字母数字字符起始和结束的“单词”,所以 \b[a-z]+\b 匹配 pie,但不匹配 pie2121pie

如果还希望匹配其他非 ASCII 字符,可以使用类似这样的正则表达式:

\b[^\W\d_]+\b

同时还允许重音字符等。您可能需要设置re.UNICODE选项,特别是在使用Python 2时,以便允许\w速记符匹配非ASCII字母。

[^\W\d_]作为一个否定的字符类,允许任何字母数字字符,除了数字和下划线。


这听起来正是我想要的,但是我无法让该死的\b正常工作。当text设置为一些普通句子时,re.findall('\b[a-z]+\b', text, re.I)什么也没有返回。无论我在方括号中放什么(或使用searchmatch),似乎都没有帮助。使用\B可以得到一些结果,但会剥离每个单词的第一个和最后一个字符。虽然听起来很懒,但我现在太累了,无法掌握新概念;你有没有可能知道它为什么不起作用?或者你能否提供一个文字上的例子,说明如何在这种情况下使用它? - orlade
5
这正是我在你的问题下留言的原因。如果你不使用原始字符串(r"\b[a-z]\b"),\b 将被解释为退格符。 - Tim Pietzcker
哦,原来是这样啊 : )。抱歉,现在已经是早上5:30了,我永远不会想到那个联系。只需添加“r”就可以完美解决问题!谢谢您,先生。 - orlade
一般情况下这个方法是有效的,但是对于包含特殊字符的单词(例如 wenn bei Beförderungen Schäden)会失败。 - yekta
@yekta:如果你使用re.UNICODE或者re.LOCALE选项编译正则表达式的话就不会有这个问题。我应该将这一点加入到我的答案中。 - Tim Pietzcker

3

你是否熟悉单词边界\b)?你可以使用\b来提取单词,并匹配其中的字母:

\b([a-zA-Z]+)\b

例如,这将获取整个单词,但在连字符、句点、分号等标记处停止。
您可以在Python手册中使用\b序列和其他内容。
此外,如果您想要匹配前面或后面的数字,可以使用负向先行/后行。
(?!\d)   # negative look-ahead for numbers
(?<!\d)  # negative look-behind for numbers

根据Tim的回答,\b听起来像是我想要的,但它并不好用。有什么想法吗?我之前尝试过前瞻和后顾,但它们似乎匹配到紧邻数字的字符,因此不能完全忽略带数字的单词。而且它还抱怨需要固定宽度模式的前瞻。 - orlade
@Pie21:那就使用单个数字匹配。我们不关心它前面或后面有多少数字,只要有一个数字即可。示例 - Brad Christie
我已经让它工作了[re.findall(r"\b([a-zA-Z]+)\b",content, re.I)],但似乎没有过滤掉正斜杠和反斜杠。这里有一些出现的单词:'[endif]', '$', '8', '/small', '/li'。 - Bill

2

关于什么:

import re
yourString="pie 42 http://foo.com GIHQ4NWL0S5SCGBDD40ZXE5IDP13TYNEA  pie42"
filter (lambda x:re.match("^[a-zA-Z]+$",x),[x for x in set(re.split("[\s:/,.:]",yourString))])

请注意:
  • split将字符串拆分为可能的候选项 => 返回一个"可能单词"列表
  • set进行唯一性过滤 => 将列表转换为集合,从而删除出现多次的条目。此步骤不是必需的。
  • filter减少了候选项的数量:接受一个列表,对每个元素应用一个测试函数,并返回成功测试的元素列表。在我们的情况下,测试函数是"匿名的"
  • lambda:匿名函数,接受一个项目并检查它是否为单词(仅限大写或小写字母)

编辑:添加了一些说明


虽然它看起来很丑,但确实有效!干杯!不过我能再请求一个帮忙吗:由于我不会使用lambda或filter,是否有一种方法可以使用re.finditer()来完成这种操作?我需要跟踪文本中每个匹配项的开始和结束索引。 - orlade

0

示例代码

print re.search(ur'(?u)ривет\b', ur'Привет')
print re.search(ur'(?u)\bривет\b', ur'Привет')

或者

s = ur"abcd ААБВ"
import re
rx1 = re.compile(ur"(?u)АБВ")
rx2 = re.compile(ur"(?u)АБВ\b")
rx3 = re.compile(ur"(?u)\bАБВ\b")
print rx1.findall(s)
print rx2.findall(s)
print rx3.findall(s)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接