我想我想做的是一项相当常见的任务,但我在网上找不到任何参考资料。我有带标点符号的文本,我想要一个单词列表。
"Hey, you - what are you doing here!?"
应该是
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']
但Python的str.split()只对一个参数有效,所以在用空格拆分后,所有单词都带有标点符号。有什么想法吗?
我想我想做的是一项相当常见的任务,但我在网上找不到任何参考资料。我有带标点符号的文本,我想要一个单词列表。
"Hey, you - what are you doing here!?"
应该是
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']
但Python的str.split()只对一个参数有效,所以在用空格拆分后,所有单词都带有标点符号。有什么想法吗?
当前回答
首先,我想同意其他人的观点,即基于正则表达式或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()
其他回答
这是我的看法。。。。
def split_string(source,splitlist):
splits = frozenset(splitlist)
l = []
s1 = ""
for c in source:
if c in splits:
if s1:
l.append(s1)
s1 = ""
else:
print s1
s1 = s1 + c
if s1:
l.append(s1)
return l
>>>out = split_string("First Name,Last Name,Street Address,City,State,Zip Code",",")
>>>print out
>>>['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']
我认为以下是满足您需求的最佳答案:
\W+可能适合这种情况,但可能不适合其他情况。
filter(None, re.compile('[ |,|\-|!|?]').split( "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标点符号。
这种方法的优点:
这种方法(与基于正则表达式的简单方法不同)可以使用任意长度的令牌(正则表达式也可以使用更高级的语法)。你不仅仅局限于象征;您可以使用任意逻辑来代替每个标记,例如,其中一个“标记”可以是一个函数,该函数根据括号的嵌套程度进行拆分。
我喜欢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
我正在重新熟悉Python,需要同样的东西。findall解决方案可能更好,但我想到了这个:
tokens = [x.strip() for x in data.split(',')]