我想我想做的是一项相当常见的任务,但我在网上找不到任何参考资料。我有带标点符号的文本,我想要一个单词列表。

"Hey, you - what are you doing here!?"

应该是

['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

但Python的str.split()只对一个参数有效,所以在用空格拆分后,所有单词都带有标点符号。有什么想法吗?


当前回答

这么多的答案,但我找不到任何能有效解决问题标题所要求的问题的解决方案(而是在多个可能的分隔符上拆分,许多答案在任何非单词上拆分,这是不同的)。因此,这是标题中问题的答案,它依赖于Python的标准和高效的重新模块:

>>> import re  # Will be splitting on: , <space> - ! ? :
>>> filter(None, re.split("[, \-!?:]+", "Hey, you - what are you doing here!?"))
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

哪里:

[…]匹配其中列出的分隔符之一,正则表达式中的\-是为了防止将-作为字符范围指示符(如a-Z)进行特殊解释,+跳过一个或多个分隔符(由于filter(),它可以省略,但这将不必要地在匹配的单字符分隔符之间产生空字符串),以及filter(None,…)删除可能由前导和尾随分隔符创建的空字符串(因为空字符串具有假布尔值)。

正如问题标题中所要求的,这个re.split()精确地“使用多个分隔符进行拆分”。

此外,该解决方案不受其他一些解决方案中单词中非ASCII字符的问题的影响(参见ghostdog74答案的第一条注释)。

re模块比“手动”执行Python循环和测试更高效(速度和简洁)!

其他回答

我遇到了类似的困境,不想使用“re”模块。

def my_split(s, seps):
    res = [s]
    for sep in seps:
        s, res = res, []
        for seq in s:
            res += seq.split(sep)
    return res

print my_split('1111  2222 3333;4444,5555;6666', [' ', ';', ','])
['1111', '', '2222', '3333', '4444', '5555', '6666']

我必须想出自己的解决方案,因为我迄今为止测试的所有东西都在某一点上失败了。

>>> import re
>>> def split_words(text):
...     rgx = re.compile(r"((?:(?<!'|\w)(?:\w-?'?)+(?<!-))|(?:(?<='|\w)(?:\w-?'?)+(?=')))")
...     return rgx.findall(text)

至少在下面的例子中,它似乎工作得很好。

>>> split_words("The hill-tops gleam in morning's spring.")
['The', 'hill-tops', 'gleam', 'in', "morning's", 'spring']
>>> split_words("I'd say it's James' 'time'.")
["I'd", 'say', "it's", "James'", 'time']
>>> split_words("tic-tac-toe's tic-tac-toe'll tic-tac'tic-tac we'll--if tic-tac")
["tic-tac-toe's", "tic-tac-toe'll", "tic-tac'tic-tac", "we'll", 'if', 'tic-tac']
>>> split_words("google.com email@google.com split_words")
['google', 'com', 'email', 'google', 'com', 'split_words']
>>> split_words("Kurt Friedrich Gödel (/ˈɡɜːrdəl/;[2] German: [ˈkʊɐ̯t ˈɡøːdl̩] (listen);")
['Kurt', 'Friedrich', 'Gödel', 'ˈɡɜːrdəl', '2', 'German', 'ˈkʊɐ', 't', 'ˈɡøːdl', 'listen']
>>> split_words("April 28, 1906 – January 14, 1978) was an Austro-Hungarian-born Austrian...")
['April', '28', '1906', 'January', '14', '1978', 'was', 'an', 'Austro-Hungarian-born', 'Austrian']

我喜欢re,但这是我没有它的解决方案:

from itertools import groupby
sep = ' ,-!?'
s = "Hey, you - what are you doing here!?"
print [''.join(g) for k, g in groupby(s, sep.__contains__) if not k]

sep__contains__是“in”运算符使用的方法。基本上与

lambda ch: ch in sep

但在这里更方便。

groupby获取字符串和函数。它使用该函数将字符串分成多个组:每当函数值发生变化时,就会生成一个新的组。因此,sep__contains__正是我们需要的。

groupby返回一个对序列,其中对[0]是我们函数的结果,对[1]是一个组。使用“if not k”,我们筛选出带有分隔符的组(因为sep.__contains__的结果在分隔符上为True)。好了,就这些了-现在我们有一个组序列,每个组都是一个单词(组实际上是一个可迭代的,所以我们使用join将其转换为字符串)。

这个解决方案非常通用,因为它使用一个函数来分隔字符串(您可以根据需要的任何条件进行拆分)。此外,它不创建中间字符串/列表(您可以删除join,因为每个组都是一个迭代器,所以表达式将变得懒惰)

这是我与多个决策者的分歧:

def msplit( str, delims ):
  w = ''
  for z in str:
    if z not in delims:
        w += z
    else:
        if len(w) > 0 :
            yield w
        w = ''
  if len(w) > 0 :
    yield w

另一种不使用正则表达式的快速方法是首先替换字符,如下所示:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']