2016-03-07 23 views
8

Ho una stringa in cui un carattere ('@') deve essere sostituito da caratteri da un elenco di uno o più caratteri "in ordine" e "periodicamente". Così, per esempio io hoQual è il modo migliore per "periodicamente" sostituire i caratteri in una stringa in Python?

'[email protected]@@[email protected]@[email protected]@@[email protected]@[email protected]'

e vogliono

'ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z'

per replace_chars = ['1', '2', '3']

Il problema è che in questo esempio ci sono più @ nella stringa di me sostituti.

Questo è il mio tentativo:

result = '' 
replace_chars = ['1', '2', '3'] 
string = '[email protected]@@[email protected]@[email protected]@@[email protected]@[email protected]' 

i = 0 
for char in string: 
    if char == '@': 
     result += replace_chars[i] 
     i += 1 
    else: 
     result += char 

print(result) 

ma questo funziona solo, naturalmente, se ci sono non più di tre @ nella stringa originale e altrimenti mi IndexError.

Modifica: Grazie per le risposte!

+2

usare 'replace_chars [i% replace_chars.length]'. poi fai solo il "modulo" della lunghezza dell'indice. per esempio. con 3 caratteri, fai '1% 3 -> 1',' 2% 3 -> 2', '3% 3 -> 0',' 4% 3 -> 1', ecc ... –

+4

Aggiungi 'i % = 3' sotto 'i + = 1' –

risposta

10

Il codice può essere corretto aggiungendo la riga i = i%len(replace_chars) come ultima riga della clausola if. In questo modo prenderai il resto dalla divisione di i in base alla lunghezza del tuo elenco di caratteri sostitutivi.

La soluzione più breve consiste nell'utilizzare un generatore che periodicamente sputa i caratteri sostitutivi.

>>> from itertools import cycle 
>>> s = '[email protected]@@[email protected]@[email protected]@@[email protected]@[email protected]' 
>>> replace_chars = ['1', '2', '3'] 
>>> 
>>> replacer = cycle(replace_chars) 
>>> ''.join([next(replacer) if c == '@' else c for c in s]) 
'ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z' 

Per ogni carattere c nella stringa di s, otteniamo il carattere di sostituzione prossimo dal generatore replacer se il personaggio è un '@', altrimenti appena ti dà il carattere originale.

Per una spiegazione perché ho usato una comprensione di lista invece di un'espressione di generatore, leggi this.

6

I generatori sono divertenti.

def gen(): 
    replace_chars = ['1', '2', '3'] 
    while True: 
     for rc in replace_chars: 
      yield rc 

with gen() as g: 
    s = '[email protected]@@[email protected]@[email protected]@@[email protected]@[email protected]' 
    s = ''.join(next(g) if c == '@' else c for c in s) 

come PM 2Ring suggerito, questo è funzionalmente uguale a itertools.cycle. La differenza è che itertools.cycle manterrà una copia aggiuntiva dell'elenco nella memoria che potrebbe non essere necessaria.

itertools.cycle fonte:

def cycle(iterable): 
    saved = [] 
    for element in iterable: 
     yield element 
     saved.append(element) 
    while saved: 
     for element in saved: 
       yield element 
+2

Il tuo' gen' è essenzialmente ciò che 'itertools.cycle' fa. –

+1

Giusto. In genere, anche se esiste un utile strumento in un pacchetto standard, mi piace scrivere l'equivalente nativo semplicemente per leggibilità. L'ipotesi è che uno non sa dello strumento di cui sopra. Spesso, ti dà semplicemente una migliore comprensione. Se vuoi usare 'itertools.cycle', sicuramente fallo. – Goodies

+0

@ PM2Ring 'cycle' salva anche gli elementi della data iterabile, perché l'iterabile potrebbe essere un iteratore che può essere esaurito - ma se' replace_chars' è garantito per essere un elenco, generatore @Goodies dovrebbe funzionare bene. – timgeb

1

Si potrebbe anche mantenere la logica indice volta che si utilizza modulo base ad una lista bozzetto utilizzando itertools.count per tenere traccia di dove siete:

from itertools import count 

cn, ln = count(), len(replace_chars) 

print("".join([replace_chars[next(cn) % ln] if c == "@" else c for c in string])) 

ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z