J'ai une chaîne où un caractère ('@') doit être remplacé par des caractères d'une liste d'un ou plusieurs caractères "dans l'ordre" et "périodiquement". Ainsi, par exemple, j'ai

'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z'

Et veux

'ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z'

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

Le problème est que dans cet exemple, il y a plus de @ dans la chaîne que de remplaçants.

Voici mon essai:

result = ''
replace_chars = ['1', '2', '3']
string = 'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z'

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

print(result)

Mais cela ne fonctionne bien sûr que s'il n'y a pas plus de trois @ dans la chaîne d'origine et sinon j'obtiens IndexError .

Edit: Merci pour les réponses!

8
FirimaElda 8 mars 2016 à 00:28

4 réponses

Meilleure réponse

Votre code pourrait être corrigé en ajoutant la ligne i = i%len(replace_chars) comme dernière ligne de votre clause if. De cette façon, vous prendrez le reste de la division de i par la longueur de votre liste de caractères de remplacement.

La solution la plus courte consiste à utiliser un générateur qui recrache périodiquement des caractères de remplacement.

>>> from itertools import cycle
>>> s = 'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z'
>>> replace_chars = ['1', '2', '3']
>>>
>>> replacer = cycle(replace_chars)
>>> ''.join([next(replacer) if c == '@' else c for c in s])
'ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z'

Pour chaque caractère c de votre chaîne s, nous obtenons le caractère de remplacement suivant du générateur replacer si le caractère est un '@', sinon il vous donne simplement le caractère original .

Pour une explication pourquoi j'ai utilisé une compréhension de liste au lieu d'une expression de générateur, lisez this .

10
Community 23 mai 2017 à 11:50

Les générateurs sont amusants.

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

with gen() as g:
    s = 'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z'
    s = ''.join(next(g) if c == '@' else c for c in s)

Comme l'a suggéré PM 2Ring, c'est la même chose que itertools.cycle. La différence est que itertools.cycle conservera une copie supplémentaire de la liste en mémoire qui peut ne pas être nécessaire.

itertools.cycle source:

def cycle(iterable):
    saved = []
    for element in iterable:
        yield element
        saved.append(element)
    while saved:
        for element in saved:
              yield element
6
Goodies 7 mars 2016 à 21:48

Je pense qu'il vaut mieux ne pas itérer caractère par caractère, en particulier pour les longues chaînes avec de longues parties sans @.

from itertools import cycle, chain

s = 'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z'
replace_chars = ['1', '2', '3']
result = ''.join(chain.from_iterable(zip(s.split('@'), cycle(replace_chars))))[:-1]

Je ne sais pas comment tuer efficacement le dernier caractère [:-1].

0
Richard Erickson 11 avril 2016 à 19:54

Vous pouvez également conserver votre logique d'index une fois que vous utilisez modulo, en utilisant une liste de composition en utilisant itertools.count pour garder une trace de l'endroit où vous vous trouvez:

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
1
Padraic Cunningham 7 mars 2016 à 22:07