2009-07-22 731 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