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

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

应该是

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

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


当前回答

使用panda的series.str.split方法可以获得相同的结果,而不是使用re-module函数re.split。

首先,使用上述字符串创建一个系列,然后将该方法应用于该系列。

thestring=pd.Series(“嘿,你-你在这里干什么!?”)thestring.str.split(pat=',|-')

参数pat接受分隔符并将拆分字符串作为数组返回。这里,使用|(或运算符)传递两个分隔符。输出如下:

[嘿,你,你在这里干什么!?]

其他回答

使用maketrans和translate,您可以轻松、整洁地完成

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()
def get_words(s):
    l = []
    w = ''
    for c in s.lower():
        if c in '-!?,. ':
            if w != '': 
                l.append(w)
            w = ''
        else:
            w = w + c
    if w != '': 
        l.append(w)
    return l

用法如下:

>>> s = "Hey, you - what are you doing here!?"
>>> print get_words(s)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']
join = lambda x: sum(x,[])  # a.k.a. flatten1([[1],[2,3],[4]]) -> [1,2,3,4]
# ...alternatively...
join = lambda lists: [x for l in lists for x in l]

然后变成三行:

fragments = [text]
for token in tokens:
    fragments = join(f.split(token) for f in fragments)

解释

这就是Haskell中所谓的列表monad。monad背后的想法是,一旦“进入monad”,你就“留在monad”直到有什么东西把你带走。例如,在Haskell中,假设您将python range(n)->[1,2,…,n]函数映射到List上。如果结果是一个List,它将被附加到List中,所以你会得到类似map(range,[3,4,1])->[0,1,2,0,1,2,3,0]的结果。这被称为map append(或mappend,或类似的东西)。这里的想法是,你已经得到了你正在应用的这个操作(对一个令牌进行拆分),每当你这样做时,你都会将结果加入到列表中。

您可以将其抽象为一个函数,并在默认情况下使用token=string.p标点符号。

这种方法的优点:

这种方法(与基于正则表达式的简单方法不同)可以使用任意长度的令牌(正则表达式也可以使用更高级的语法)。你不仅仅局限于象征;您可以使用任意逻辑来代替每个标记,例如,其中一个“标记”可以是一个函数,该函数根据括号的嵌套程度进行拆分。

首先,我想同意其他人的观点,即基于正则表达式或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()

使用替换两次:

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

结果是:

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