23

我需要使用每个字符作为分隔符string.punctuation来拆分数据字符串。string.whitespace

此外,我需要将分隔符保留在输出列表中,在它们在字符串中分隔的项目之间。

例如,

"Now is the winter of our discontent"

应该输出:

['Now', ' ', 'is', ' ', 'the', ' ', 'winter', ' ', 'of', ' ', 'our', ' ', 'discontent']

如果不求助于嵌套循环的狂欢,我不确定如何做到这一点,这非常慢。我该怎么做?

4

9 回答 9

21

与其他方法不同的非正则表达式方法:

>>> import string
>>> from itertools import groupby
>>> 
>>> special = set(string.punctuation + string.whitespace)
>>> s = "One two  three    tab\ttabandspace\t end"
>>> 
>>> split_combined = [''.join(g) for k, g in groupby(s, lambda c: c in special)]
>>> split_combined
['One', ' ', 'two', '  ', 'three', '    ', 'tab', '\t', 'tabandspace', '\t ', 'end']
>>> split_separated = [''.join(g) for k, g in groupby(s, lambda c: c if c in special else False)]
>>> split_separated
['One', ' ', 'two', '  ', 'three', '    ', 'tab', '\t', 'tabandspace', '\t', ' ', 'end']

我猜可以使用dict.fromkeysand.get而不是。lambda

[编辑]

一些解释:

groupby接受两个参数,一个可迭代的和一个(可选的)keyfunction。它循环遍历 iterable 并将它们与 keyfunction 的值分组:

>>> groupby("sentence", lambda c: c in 'nt')
<itertools.groupby object at 0x9805af4>
>>> [(k, list(g)) for k,g in groupby("sentence", lambda c: c in 'nt')]
[(False, ['s', 'e']), (True, ['n', 't']), (False, ['e']), (True, ['n']), (False, ['c', 'e'])]

其中具有连续键函数值的术语被组合在一起。(实际上,这是一个常见的错误来源——人们忘记了如果他们想要对可能不连续的术语进行分组,他们必须首先按 keyfunc 排序。)

正如@JonClements 猜测的那样,我想到的是

>>> special = dict.fromkeys(string.punctuation + string.whitespace, True)
>>> s = "One two  three    tab\ttabandspace\t end"
>>> [''.join(g) for k,g in groupby(s, special.get)]
['One', ' ', 'two', '  ', 'three', '    ', 'tab', '\t', 'tabandspace', '\t ', 'end']

对于我们合并分隔符的情况。 如果值不在字典中,则.get返回。None

于 2012-11-01T22:08:24.247 回答
7
import re
import string

p = re.compile("[^{0}]+|[{0}]+".format(re.escape(
    string.punctuation + string.whitespace)))

print p.findall("Now is the winter of our discontent")

我不喜欢使用正则表达式来解决所有问题,但如果你想要它又快又短,我认为你在这方面没有太多选择。

我会解释正则表达式,因为你不熟悉它:

  • [...]表示方括号内的任何字符
  • [^...]表示任何不在方括号内的字符
  • +后面的意思是前面的一个或多个
  • x|y表示匹配xy

因此,正则表达式匹配 1 个或多个字符,其中所有字符都必须是标点符号和空格,或者不能。该findall方法查找模式的所有非重叠匹配。

于 2012-11-01T21:56:33.583 回答
4

试试这个:

import re
re.split('(['+re.escape(string.punctuation + string.whitespace)+']+)',"Now is the winter of our discontent")

Python文档的解释:

如果在模式中使用捕获括号,则模式中所有组的文本也会作为结果列表的一部分返回。

于 2012-11-01T21:58:23.973 回答
3

线性 ( O(n)) 时间内的解:

假设你有一个字符串:

original = "a, b...c    d"

首先将所有分隔符转换为空格:

splitters = string.punctuation + string.whitespace
trans = string.maketrans(splitters, ' ' * len(splitters))
s = original.translate(trans)

现在s == 'a b c d'。现在您可以使用itertools.groupby在空格和非空格之间交替:

result = []
position = 0
for _, letters in itertools.groupby(s, lambda c: c == ' '):
    letter_count = len(list(letters))
    result.append(original[position:position + letter_count])
    position += letter_count

现在result == ['a', ', ', 'b', '...', 'c', ' ', 'd'],这就是您所需要的。

于 2012-11-01T22:04:30.757 回答
1

根据您正在处理的文本,您可以将分隔符的概念简化为“除字母和数字之外的任何内容”。如果这可行,您可以使用以下正则表达式解决方案:

re.findall(r'[a-zA-Z\d]+|[^a-zA-Z\d]', text)

这假设您想要拆分每个单独的分隔符,即使它们连续出现,所以'foo..bar'会变成['foo', '.', '.', 'bar']. 相反,如果您期望['foo', '..', 'bar'],请使用[a-zA-Z\d]+|[^a-zA-Z\d]+(唯一的区别是+在最后添加)。

于 2012-11-01T22:02:09.863 回答
1

我的看法:

from string import whitespace, punctuation
import re

pattern = re.escape(whitespace + punctuation)
print re.split('([' + pattern + '])', 'now is the winter of')
于 2012-11-01T22:07:01.613 回答
0
from string import punctuation, whitespace

s = "..test. and stuff"

f = lambda s, c: s + ' ' + c + ' ' if c in punctuation else s + c
l =  sum([reduce(f, word).split() for word in s.split()], [])

print l
于 2012-11-01T21:57:07.437 回答
0

对于任意的分隔符集合:

def separate(myStr, seps):
    answer = []
    temp = []
    for char in myStr:
        if char in seps:
            answer.append(''.join(temp))
            answer.append(char)
            temp = []
        else:
            temp.append(char)
    answer.append(''.join(temp))
    return answer

In [4]: print separate("Now is the winter of our discontent", set(' '))
['Now', ' ', 'is', ' ', 'the', ' ', 'winter', ' ', 'of', ' ', 'our', ' ', 'discontent']

In [5]: print separate("Now, really - it is the winter of our discontent", set(' ,-'))
['Now', ',', '', ' ', 'really', ' ', '', '-', '', ' ', 'it', ' ', 'is', ' ', 'the', ' ', 'winter', ' ', 'of', ' ', 'our', ' ', 'discontent']

希望这可以帮助

于 2012-11-01T22:18:04.390 回答
-1
from itertools import chain, cycle, izip

s = "Now is the winter of our discontent"
words = s.split()

wordsWithWhitespace = list( chain.from_iterable( izip( words, cycle([" "]) ) ) )
# result : ['Now', ' ', 'is', ' ', 'the', ' ', 'winter', ' ', 'of', ' ', 'our', ' ', 'discontent', ' ']
于 2012-11-01T22:07:39.023 回答