2016-03-07 6 views
8

Mam ciąg znaków, w którym znak ("@") musi być zastąpiony przez znaki z listy jednego lub więcej znaków "w kolejności" i "okresowo". Tak na przykład mamJaki jest najlepszy sposób "okresowego" zastępowania znaków w łańcuchu w Pythonie?

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

i chcą

'ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z'

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

Problemem jest to, że w tym przykładzie jest więcej @ w ciągu niż mam zamienniki.

To moja próba:

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) 

ale działa to tylko oczywiście jeśli istnieje nie więcej niż trzy @ w oryginalnym ciąg a poza tym mam IndexError.

Edytuj: Dziękujemy za odpowiedzi!

+2

użyj 'replace_chars [i% replace_chars.length]'. wtedy robisz tylko "modulo" długości w indeksie. na przykład z trzema znakami: '1% 3 -> 1',' 2% 3 -> 2', '3% 3 -> 0',' 4% 3 -> 1', itd ... –

+4

Dodaj 'i % = 3 "poniżej' i + = 1' –

Odpowiedz

10

Twój kod można naprawić, dodając wiersz i = i%len(replace_chars) jako ostatni wiersz klauzuli if. W ten sposób otrzymasz resztę z podziału i według długości listy zastępczych znaków.

Krótsze rozwiązanie polega na użyciu generatora, który okresowo wypluwa zastępcze znaki.

>>> 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' 

Dla każdego znaku c w Twoim ciąg s, otrzymujemy kolejną zastępczą znak z generatora replacer jeśli postać jest '@', w przeciwnym razie po prostu daje oryginalny charakter.

Aby uzyskać wyjaśnienie, dlaczego użyłem rozumienia listy zamiast wyrażenia generatora, przeczytaj this.

6

Generatory są zabawne.

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) 

Zgodnie z sugestią PM 2Ring, funkcjonalnie jest taka sama jak itertools.cycle. Różnica polega na tym, że itertools.cycle będzie przechowywać dodatkową kopię listy w pamięci, która może nie być konieczna.

itertools.cycle źródło:

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

Twój 'gen' jest w zasadzie tym, co robi' itertools.cycle'. –

+1

W prawo. Zwykle, nawet jeśli istnieje użyteczne narzędzie w standardowym pakiecie, lubię pisać natywny odpowiednik po prostu dla czytelności. W założeniu nie wiadomo o wyżej wspomnianym narzędziu. Często to tylko daje lepsze zrozumienie. Jeśli chcesz użyć 'itertools.cycle', zdecydowanie zrób to. – Goodies

+0

@ PM2Ring 'cycle' również zapisuje elementy z podanej iteracji, ponieważ iteracja może być iteratorem, który może zostać wyczerpany - ale jeśli' replace_chars' ma gwarantowaną listę, generator @Goodies powinien działać poprawnie. – timgeb

1

Można również zachować swoją logikę indeksu raz użyć modulo podstawie listy komp za pomocą itertools.count śledzić, gdzie jesteś:

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 
Powiązane problemy