2009-07-22 8 views
24

Plus tôt aujourd'hui, j'avais besoin d'itérer sur une chaîne de 2 caractères à la fois pour analyser une chaîne formatée comme "+c-R+D-E" (il y a quelques lettres supplémentaires). Je me suis retrouvé avec ça, ça marche, mais ça a l'air moche. J'ai fini par commenter ce qu'il faisait parce que cela ne me semblait pas évident. Il semble presque pythonique, mais pas tout à fait.Itérer sur une chaîne 2 (ou n) caractères à la fois en Python

# 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 

Y a-t-il des façons meilleures/plus propres de faire cela?

+0

@Richard, peut-être rater un ")" sur ligne 2? – sunqiang

+0

duplication possible de [Quelle est la manière la plus "pythonique" d'itérer sur une liste en morceaux?] (Http://stackoverflow.com/questions/434287/what-is-the-most-pythonic-way-to-iterate -over-a-list-in-chunks) –

Répondre

38

J'sais au sujet propre, mais il y a une autre alternative:

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

Une version non-copie :

from itertools import izip, islice 
for (op, code) in izip(islice(s, 0, None, 2), islice(s, 1, None, 2)): 
    print op, code 
+0

J'aime vraiment celle-ci ... je souhaite juste –

1
>>> s = "+c-R+D-e" 
>>> s 
'+c-R+D-e' 
>>> s[::2] 
'+-+-' 
>>> 
13

Peut-être que ce serait plus propre?

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

Vous pourriez peut-être écrire un générateur pour faire ce que vous voulez, ce serait peut-être plus pythonique :)

+0

+1 simple et cela fonctionne pour tout n (si l'exception ValueError est gérée quand len (s) n'est pas un multiple de N. – mhawke

+0

Ne fonctionnera pas pour les chaînes de longueur impaire – bcoughlan

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 nécessite Python 2.6 (ou supérieur). Si sur Python 2.4 ou 2.5, utilisez la définition de izip_longest du document ou changer la fonction de mérou à:

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

Meilleure réponse, sauf qu'elle est renommée 'zip_longest' [dans Python3] (https://docs.python.org/3.4/library/itertools.html#itertools.zip_longest) – cdunn2001

2

les autres réponses fonctionnent bien pour n = 2, mais pour le cas général, vous pouvez essayer ceci:

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

Grande opportunité pour un générateur. Pour les listes plus volumineuses, cela sera beaucoup plus efficace que de compresser tous les autres éléments. Notez que cette version gère également les chaînes avec ballants op de

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: Réécriture mineure pour éviter des exceptions ValueError.

+0

Quelques cas de bords - soulève toujours ValueError: essayez les opcodes ("a1") – mhawke

+0

Merci de m'avoir signalé cela - j'ai réécrit. – Triptych

4

Triptych inspiré cette solution plus générale:

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

Peut-être pas le plus efficace, mais si vous aimez regexes ...

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

Cette approche en charge un nombre arbitraire d'éléments par résultat, évalue paresseusement, et l'entrée itérable peut être un générateur (aucune indexation n'est tentée):

import itertools 

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

Les éléments restants sont renvoyés dans une liste plus courte.

Exemple d'utilisation:

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

je suis tombé sur un problème similaire.Terminé de faire quelque chose comme ceci:

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

    print op, code 

Je pensais que c'était le plus lisible.

0

Voilà ma réponse, un peu plus propre de peu à mes yeux:

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

Tenir compte pip installation more_itertools, qui déjà livré avec une mise en œuvre chunked ainsi que d'autres outils utiles:

import more_itertools 

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

sortie:

+ c 
- R 
+ D 
- e 
Questions connexes