Iterate over a string 2 (or n) characters at a tim

2019-01-17 15:02发布

Earlier today I needed to iterate over a string 2 characters at a time for parsing a string formatted like "+c-R+D-E" (there are a few extra letters).

I ended up with this, which works, but it looks ugly. I ended up commenting what it was doing because it felt non-obvious. It almost seems pythonic, but not quite.

# 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

Are there some better/cleaner ways to do this?

12条回答
放荡不羁爱自由
2楼-- · 2019-01-17 15:14

Consider pip installing more_itertools, which already ships with a chunked implementation along with other helpful tools:

import more_itertools 

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

Output:

+ c
- R
+ D
- e
查看更多
Fickle 薄情
3楼-- · 2019-01-17 15:17

Maybe not the most efficient, but if you like regexes...

import re
s = "+c-R+D-e"
for op, code in re.findall('(.)(.)', s):
    print op, code
查看更多
爷、活的狠高调
4楼-- · 2019-01-17 15:18

Great opportunity for a generator. For larger lists, this will be much more efficient than zipping every other elemnent. Note that this version also handles strings with dangling ops

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

edit: minor rewrite to avoid ValueError exceptions.

查看更多
神经病院院长
5楼-- · 2019-01-17 15:20
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 requires Python 2.6( or higher). If on Python 2.4 or 2.5, use the definition for izip_longest from the document or change the grouper function to:

from itertools import izip, chain, repeat
def grouper(iterable, n, padvalue=None):
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)
查看更多
不美不萌又怎样
6楼-- · 2019-01-17 15:23
>>> s = "+c-R+D-e"
>>> s
'+c-R+D-e'
>>> s[::2]
'+-+-'
>>>
查看更多
我只想做你的唯一
7楼-- · 2019-01-17 15:27

Dunno about cleaner, but there's another alternative:

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

A no-copy version:

from itertools import izip, islice
for (op, code) in izip(islice(s, 0, None, 2), islice(s, 1, None, 2)):
    print op, code
查看更多
登录 后发表回答