簡體   English   中英

使用多個分隔符有效地分割字符串並保留每個分隔符?

[英]Efficiently split a string using multiple separators and retaining each separator?

我需要使用string.punctuationstring.whitespace中的每個字符作為分隔符來分割數據字符串。

此外,我需要將分隔符保留在輸出列表中,在它們在字符串中分隔的項之間。

例如,

"Now is the winter of our discontent"

應該輸出:

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

我不知道如何在不訴諸嵌套循環的狂歡的情況下如何做到這一點,這是不可接受的慢。 我該怎么做?

與其他人不同的非正則表達式方法:

>>> 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.fromkeys.get而不是lambda

[編輯]

一些解釋:

groupby接受兩個參數,一個是iterable和一個(可選的)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']

對於我們組合分離器的情況。 如果值不在dict中,則.get返回None

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方法查找模式的所有非重疊匹配。

嘗試這個:

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

Python文檔中的說明

如果在模式中使用捕獲括號,則模式中所有組的文本也將作為結果列表的一部分返回。

線性( O(n) )時間的解決方案:

假設你有一個字符串:

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

首先將所有分隔符轉換為空格:

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

現在s == 'abc 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'] ,這就是你所需要的。

根據您正在處理的文本,您可以將分隔符的概念簡化為“除字母和數字之外的任何內容”。 如果這樣可行,您可以使用以下正則表達式解決方案:

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]+ (唯一的區別是增加+在最后)。

我的看法:

from string import whitespace, punctuation
import re

pattern = re.escape(whitespace + punctuation)
print re.split('([' + pattern + '])', 'now is the winter of')
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

對於任意分隔符集合:

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']

希望這可以幫助

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', ' ']

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM