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

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

应该是

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

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


当前回答

我最喜欢替换方式。以下过程将字符串拆分列表中定义的所有分隔符更改为拆分列表中的第一个分隔符,然后在该分隔符上拆分文本。它还说明了splitlist是否恰好是空字符串。它返回一个单词列表,其中没有空字符串。

def split_string(text, splitlist):
    for sep in splitlist:
        text = text.replace(sep, splitlist[0])
    return filter(None, text.split(splitlist[0])) if splitlist else [text]

其他回答

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

>>> 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']

使用替换两次:

a = '11223FROM33344INTO33222FROM3344'
a.replace('FROM', ',,,').replace('INTO', ',,,').split(',,,')

结果是:

['11223', '33344', '33222', '3344']

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

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

创建一个函数,将两个字符串(要拆分的源字符串和分隔符的拆分列表字符串)作为输入,并输出拆分单词列表:

def split_string(source, splitlist):
    output = []  # output list of cleaned words
    atsplit = True
    for char in source:
        if char in splitlist:
            atsplit = True
        else:
            if atsplit:
                output.append(char)  # append new word after split
                atsplit = False
            else: 
                output[-1] = output[-1] + char  # continue copying characters until next split
    return output

首先,我想同意其他人的观点,即基于正则表达式或str.translate(…)的解决方案是最具性能的。对于我的用例,这个函数的性能并不重要,所以我想添加一些我认为符合这个标准的想法。

我的主要目标是将其他一些答案中的想法归纳为一个解决方案,该解决方案可以适用于包含不仅仅是正则表达式单词的字符串(即,将标点符号字符的显式子集列入黑名单,而不是将单词字符列入白名单)。

注意,在任何方法中,也可以考虑使用string.p标点符号代替手动定义的列表。

选项1-re.sub

我很惊讶地发现到目前为止还没有使用re.sub(…)的答案。我发现这是解决这个问题的一种简单而自然的方法。

import re

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

words = re.split(r'\s+', re.sub(r'[,\-!?]', ' ', my_str).strip())

在这个解决方案中,我将对re.sub(…)的调用嵌套在re.split(…)内部-但如果性能很关键,那么在外部编译正则表达式可能会很有用-对于我的用例来说,差异并不大,所以我更喜欢简单性和可读性。

选项2-str.replace

这是几行,但它的优点是可以扩展,而不必检查是否需要转义正则表达式中的某个字符。

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

replacements = (',', '-', '!', '?')
for r in replacements:
    my_str = my_str.replace(r, ' ')

words = my_str.split()

如果能够将str.replace映射到字符串,那会很好,但我不认为这可以用不可变的字符串来实现,虽然映射到一个字符列表是可行的,但对每个字符运行每个替换听起来都很过分。(编辑:有关功能示例,请参见下一个选项。)

选项3-functools.reduce

(在Python 2中,reduce在全局命名空间中可用,而无需从functools导入。)

import functools

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

replacements = (',', '-', '!', '?')
my_str = functools.reduce(lambda s, sep: s.replace(sep, ' '), replacements, my_str)
words = my_str.split()