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

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

应该是

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

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


当前回答

首先,我不认为您的意图是在拆分函数中实际使用标点符号作为分隔符。您的描述表明您只是想从生成的字符串中删除标点符号。

我经常遇到这种情况,我通常的解决方案不需要re。

单行lambda函数,带列表理解:

(需要导入字符串):

split_without_punc = lambda text : [word.strip(string.punctuation) for word in 
    text.split() if word.strip(string.punctuation) != '']

# Call function
split_without_punc("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

功能(传统)

作为传统函数,这仍然只有两行具有列表理解(除了导入字符串):

def split_without_punctuation2(text):

    # Split by whitespace
    words = text.split()

    # Strip punctuation from each word
    return [word.strip(ignore) for word in words if word.strip(ignore) != '']

split_without_punctuation2("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

它也会自然地保留缩略词和连字符。您可以始终使用text.replace(“-”,“”)在拆分前将连字符转换为空格。

不带Lambda或列表理解的通用函数

对于更一般的解决方案(可以指定要删除的字符),并且不需要列表理解,您可以得到:

def split_without(text: str, ignore: str) -> list:

    # Split by whitespace
    split_string = text.split()

    # Strip any characters in the ignore string, and ignore empty strings
    words = []
    for word in split_string:
        word = word.strip(ignore)
        if word != '':
            words.append(word)

    return words

# Situation-specific call to general function
import string
final_text = split_without("Hey, you - what are you doing?!", string.punctuation)
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

当然,您也可以将lambda函数推广到任何指定的字符串。

其他回答

遇到与@ooboo相同的问题并找到此主题@ghostdog74启发了我,也许有人觉得我的解决方案很有用

str1='adj:sg:nom:m1.m2.m3:pos'
splitat=':.'
''.join([ s if s not in splitat else ' ' for s in str1]).split()

在空格处输入内容,如果不想在空格处拆分,请使用相同的字符进行拆分。

正则表达式对正的情况:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

我喜欢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,因为每个组都是一个迭代器,所以表达式将变得懒惰)

专业提示:使用string.translate进行Python最快的字符串操作。

一些证据。。。

首先,缓慢的方式(抱歉pprzemek):

>>> import timeit
>>> S = 'Hey, you - what are you doing here!?'
>>> def my_split(s, seps):
...     res = [s]
...     for sep in seps:
...         s, res = res, []
...         for seq in s:
...             res += seq.split(sep)
...     return res
... 
>>> timeit.Timer('my_split(S, punctuation)', 'from __main__ import S,my_split; from string import punctuation').timeit()
54.65477919578552

接下来,我们使用re.findall()(如建议的答案所示)。更快:

>>> timeit.Timer('findall(r"\w+", S)', 'from __main__ import S; from re import findall').timeit()
4.194725036621094

最后,我们使用translate:

>>> from string import translate,maketrans,punctuation 
>>> T = maketrans(punctuation, ' '*len(punctuation))
>>> timeit.Timer('translate(S, T).split()', 'from __main__ import S,T,translate').timeit()
1.2835021018981934

说明:

string.translate是用C实现的,与Python中的许多字符串操作函数不同,string.ttranslate不会生成新字符串。所以它的速度和字符串替换一样快。

不过,这有点尴尬,因为它需要一个翻译表来实现这一魔术。您可以使用maketrans()方便函数创建转换表。这里的目标是将所有不需要的字符转换为空格。一换一的替代品。同样,不会产生新数据。所以这很快!

接下来,我们使用旧的split()。默认情况下,split()将对所有空白字符进行操作,将它们分组以进行拆分。结果将是您想要的单词列表。而且这种方法几乎比re.findall()快4倍!

这么多的答案,但我找不到任何能有效解决问题标题所要求的问题的解决方案(而是在多个可能的分隔符上拆分,许多答案在任何非单词上拆分,这是不同的)。因此,这是标题中问题的答案,它依赖于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循环和测试更高效(速度和简洁)!