Заменить каждую n-ю букву в строке

Я пишу функцию для замены каждой n-й буквы из строки

def replaceN(str, n):
   for i in range(len(str)):
     n=str[i]
     newStr=str.replace(n, "*")
     return newStr

но когда я его выполняю, только первая буква заменяется на *. Я уверен, что есть ошибка, но я не вижу ее.

7 ответов

Один лайнер:

newstring = ''.join("*" if i % n == 0 else char for i, char in enumerate(string, 1))

Expanded:

def replace_n(string, n, first=0):
    letters = (
        # i % n == 0 means this letter should be replaced
        "*" if i % n == 0 else char

        # iterate index/value pairs
        for i, char in enumerate(string, -first)
    )
    return ''.join(letters)
>>> replace_n("hello world", 4)
'*ell* wo*ld'
>>> replace_n("hello world", 4, first=-1)
'hel*o w*orl*'

У вашего кода есть несколько проблем:

Во-первых, return в неправильном месте. Он находится внутри цикла for, но он должен быть снаружи. Далее в следующем фрагменте:

for i in range(len(str)):
    n=str[i]
    newStr=str.replace(n, "*")

n то, что вы передали в качестве второго аргумента вашей функции, перезаписывается на каждом шаге цикла. Так что, если ваша начальная строка - "abcabcabcd", и вы передаете n=3 (число) в качестве второго аргумента, ваш цикл будет:

n="a"
n="b"
n="c"
...

поэтому значение 3 никогда не используется. Кроме того, в вашем цикле сохраняется только последняя замена, выполненная в вашей строке:

n="a"
newStr="abcabcabcd".replace("a", "*") --> newStr = "*bc*bc*bcd"
n="b"
newStr="abcabcabcd".replace("b", "*") --> newStr = "a*ca*ca*cd"
...
n="d"
newStr="abcabcabcd".replace("d", "*") --> newStr = "abcabcabc*"

Если вы проверите свою функцию (после исправления return положение) с некоторыми строками вроде бы нормально работает:

In [7]: replaceN("abcabcabc", 3)
Out[7]: 'ab*ab*ab*'

но если вы сделаете выбор более тщательно:

In [10]: replaceN("abcabcabcd", 3)
Out[10]: 'abcabcabc*'

тогда очевидно, что код завершается ошибкой, и это эквивалентно замене только последнего символа вашей строки:

my_string.replace(my_string[-1], "*")

Код, данный Эриком, работает нормально:

In [16]: ''.join("*" if i % 3 == 0 else char for i, char in enumerate("abcabcabcd"))
Out[16]: '*bc*bc*bc*'

Он заменяет позиции 3, 6, 9 и т. Д. Может потребоваться некоторая корректировка, если вы не хотите, чтобы позиция 0 тоже менялась.

Попробуй это:

def replaceN(string, n):
    s = ''
    for i in range(len(string)):
        if i%n!=0:
            s += string[i]
        else:
            s += '*'
    return s

Вы поместили свой возврат в цикл, поэтому после первой итерации он возвращает строку без замены остальной части строки. Строка должна быть возвращена после завершения цикла. Примерно так должно работать:

def replaceN(str, n):
    for i in range(len(str)):
        n=str[i]
        newStr=str.replace(n, "*")
    return newStr

Другой вариант, используя re:

import re
def repl(matchobj):
    return re.sub('.$','*',matchobj.group(0))
def replaceN(str, n):
    return re.sub('.'*n,repl,str)

Однако для этого нужна дополнительная библиотека. Если эта библиотека уже импортирована, это может быть сокращенный метод.

Простой способ переопределить строку каждый раз:

def replaceN(string, n):
    for i in range(n, len(string), n):
        string = string[:i-1] + "*" + string[i:]
    return string


In [1]: replaceN("abcabcabc", 3)
Out[1]: ab*ab*ab*
In [2]: replaceN("abcd abcd abcd", 4)
Out[2]: abcd*abcd*abcd

Мне нравится ответ Эрика, но вы указали в комментарии, что первое письмо не должно заменяться. Вы можете адаптировать код следующим образом:

''.join("*" if i % n == 0 else char for i, char in enumerate(string, 1))
                                             the difference is here  ^

def replaceN(s, n):
    return ''.join(
      '*' if not i%n else char for i, char in enumerate(s, 1))

>>> replaceN('welcome', 3)
'we*co*e'
Другие вопросы по тегам