2009-07-22 728 views
24

今天早些時候,我需要每次迭代字符串2個字符來解析格式爲"+c-R+D-E"(還有一些額外的字母)的字符串。在Python中一次遍歷字符串2(或n)個字符

我結束了這個工作,但它看起來很醜。我最終評論它在做什麼,因爲它感覺不明顯。它幾乎似乎pythonic,但不完全。

# Might not be exact, but you get the idea, use the step 
# parameter of range() and slicing to grab 2 chars at a time 
s = "+c-R+D-e" 
for op, code in (s[i:i+2] for i in range(0, len(s), 2)): 
    print op, code 

是否有一些更好/更乾淨的方法來做到這一點?

+0

@理查德,可能是你想念一個「)」第2行? – sunqiang 2009-07-22 01:38:21

+0

可能的重複[什麼是最「pythonic」的方式來遍歷一個列表在塊?](http://stackoverflow.com/questions/434287/what-is-the-most-pythonic-way-to-iterate -over-a-list-in-chunks) – 2014-06-28 22:34:10

回答

38

說不上約清潔,但還有另一種選擇:

for (op, code) in zip(s[0::2], s[1::2]): 
    print op, code 

不復製版本:

from itertools import izip, islice 
for (op, code) in izip(islice(s, 0, None, 2), islice(s, 1, None, 2)): 
    print op, code 
1
>>> s = "+c-R+D-e" 
>>> s 
'+c-R+D-e' 
>>> s[::2] 
'+-+-' 
>>> 
13

也許這會更乾淨?

s = "+c-R+D-e" 
for i in xrange(0, len(s), 2): 
    op, code = s[i:i+2] 
    print op, code 

也許你可以寫一個生成器做你想要什麼,也許這將是更Python :)

+0

+1簡單並適用於任何n(如果在len(s)不是n的倍數時處理ValueError異常。 – mhawke 2009-07-22 03:04:52

+0

不適用於奇數長度的字符串 – bcoughlan 2013-07-02 22:48:10

4
from itertools import izip_longest 
def grouper(iterable, n, fillvalue=None): 
    args = [iter(iterable)] * n 
    return izip_longest(*args, fillvalue=fillvalue) 
def main(): 
    s = "+c-R+D-e" 
    for item in grouper(s, 2): 
     print ' '.join(item) 
if __name__ == "__main__": 
    main() 
##output 
##+ c 
##- R 
##+ D 
##- e 

izip_longest需要Python 2.6(或更高版本)。如果是Python 2.4或2.5,使用定義爲izip_longestdocument或改變石斑魚功能:

from itertools import izip, chain, repeat 
def grouper(iterable, n, padvalue=None): 
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n) 
+1

最好的答案,除了它被重命名爲`zip_longest` [在Python3](https://docs.python.org/3.4/library/itertools.html#itertools.zip_longest)。 – cdunn2001 2014-07-08 19:29:17

2

其他的答案很好地工作對於n = 2,但是一般情況下,你可以試試這個:

def slicen(s, n, truncate=False): 
    nslices = len(s)/n 
    if not truncate and (len(s) % n): 
     nslices += 1 
    return (s[i*n:n*(i+1)] for i in range(nslices)) 

>>> s = '+c-R+D-e' 
>>> for op, code in slicen(s, 2): 
...  print op, code 
... 
+ c 
- R 
+ D 
- e 

>>> for a, b, c in slicen(s, 3): 
...  print a, b, c 
... 
+ c - 
R + D 
Traceback (most recent call last): 
    File "<stdin>", line 1, in ? 
ValueError: need more than 2 values to unpack 

>>> for a, b, c in slicen(s,3,True): 
...  print a, b, c 
... 
+ c - 
R + D 
2

發電機的好機會。對於更大的列表,這將比壓縮其他所有元素更有效率。請注意,此版本還負責與叼着op小號

def opcodes(s): 
    while True: 
     try: 
      op = s[0] 
      code = s[1] 
      s = s[2:] 
     except IndexError: 
      return 
     yield op,code   


for op,code in opcodes("+c-R+D-e"): 
    print op,code 

編輯字符串:輕微重寫,以避免ValueError異常例外。

4

Triptych激發了更廣泛的解決方案:

def slicen(s, n, truncate=False): 
    assert n > 0 
    while len(s) >= n: 
     yield s[:n] 
     s = s[n:] 
    if len(s) and not truncate: 
     yield s 

for op, code in slicen("+c-R+D-e", 2): 
    print op,code 
0

也許不是最有效的,但如果你喜歡的正則表達式...

import re 
s = "+c-R+D-e" 
for op, code in re.findall('(.)(.)', s): 
    print op, code 
2

這種方法支持每個結果元素的任意號碼,懶惰地評估,並且輸入迭代可以是發生器(不嘗試索引):

import itertools 

def groups_of_n(n, iterable): 
    c = itertools.count() 
    for _, gen in itertools.groupby(iterable, lambda x: c.next()/n): 
     yield gen 

任何遺留元素都會以較短的列表返回。

實例:

for g in groups_of_n(4, xrange(21)): 
    print list(g) 

[0, 1, 2, 3] 
[4, 5, 6, 7] 
[8, 9, 10, 11] 
[12, 13, 14, 15] 
[16, 17, 18, 19] 
[20] 
0

我遇到了一個類似的問題。結束這樣的事情:

ops = iter("+c-R+D-e") 
for op in ops 
    code = ops.next() 

    print op, code 

我覺得這是最可讀的。

0

這裏是我的答案,一點點清潔我的眼簾:

for i in range(0, len(string) - 1): 
    if i % 2 == 0: 
     print string[i:i+2] 
0

考慮pip安裝more_itertools,已經附帶其他有用的工具沿着chunked實現:

import more_itertools 

for op, code in more_itertools.chunked(s, 2): 
    print(op, code) 

輸出:

+ c 
- R 
+ D 
- e