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

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

应该是

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

但Python的str.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。

单行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函数推广到任何指定的字符串。

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标点符号。

这种方法的优点:

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

我认为以下是满足您需求的最佳答案:

\W+可能适合这种情况,但可能不适合其他情况。

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")

我喜欢pprzemek的解决方案,因为它不假设分隔符是单个字符,也不试图利用正则表达式(如果分隔符的数量太长,这将不会很好地工作)。

为清晰起见,以下是上述解决方案的更可读版本:

def split_string_on_multiple_separators(input_string, separators):
    buffer = [input_string]
    for sep in separators:
        strings = buffer
        buffer = []  # reset the buffer
        for s in strings:
            buffer = buffer + s.split(sep)

    return buffer

实现这一点的另一种方法是使用自然语言工具包(nltk)。

import nltk
data= "Hey, you - what are you doing here!?"
word_tokens = nltk.tokenize.regexp_tokenize(data, r'\w+')
print word_tokens

这张照片显示:[“嘿”、“你”、“什么”、“是”、“您”、“正在做”、“在这里”]

这种方法的最大缺点是需要安装nltk包。

好处是,一旦获得令牌,就可以使用nltk包的其余部分做很多有趣的事情。