Hi,

I am trying to write a python func. which can replace nth occurance of any substring present in any string.
For example:

main_string = "badZZZZZZZZZZZZZZZZZZbadZZZZZZZZZZZZZZZZZZZZzbadZZZZZZZZZZZZZZZZZ"

If I wanna replace the 2nd occurance of string "bad" with 'good' i will pass

customreplace(main_string,'bad','good',2)

So the Function should return

badZZZZZZZZZZZZZZZZZZgoodZZZZZZZZZZZZZZZZZZZZzbadZZZZZZZZZZZZZZZZZ

I have tried normal replace()

main_string.replace('bad','good',2)

. But its not what I wanted.

Thanks In Advance,

Dani AI

Generated

str.replace(x, n) replaces the first n occurrences, not specifically the nth one. If you only want to change the nth occurrence, you can leverage str.split with maxsplit to cut the string at the correct boundary and then stitch it back together with a single replacement:

def replace_nth(s, sub, repl, n):
    """
    Replace only the nth (1-based) occurrence of 'sub' in 's' with 'repl'.
    Returns the original string if 'sub' occurs fewer than n times.
    """
    if n <= 0:
        raise ValueError("n must be >= 1")
    if not sub:
        raise ValueError("'sub' must be a non-empty string")

    parts = s.split(sub, n)
    if len(parts) <= n:
        return s  # not enough occurrences
    return sub.join(parts[:-1]) + repl + parts[-1]

This runs in O(len(s)) time, preserves the first n-1 occurrences, replaces only the nth, and leaves later ones untouched. It also avoids regex entirely. If you need the nth from the end, use rsplit(sub, n) instead of split.

Note: This treats matches as non-overlapping, just like normal substring operations. If you need overlapping matches or pattern-based matching, a regex approach (for example, re.sub with a callable that counts matches) works well; see the count/callable behavior documented in re.sub. For the string-based approach above, split/maxsplit is documented at str.split.

Recommended Answers

All 4 Replies

One way to do it would be to use re.sub with a function argument. That function could increase a counter every time it's called and then either return the given string unchanged or return the replacement string depending on whether the counter is equal to your n.

I tried Something like this, But the output is not what I wnated..

import re
main_string = "badZZZZZZZZZZZZZZZZZZbadZZZZZZZZZZZZZZZZZZZZzbadZZZZZZZZZZZZZZZZZ"
for i in range(0,main_string.count('bad')):
        print re.sub(r'bad','good',main_string,i)

Out Put is :

debasish@debasish:~$ python occ.py 
goodZZZZZZZZZZZZZZZZZZgoodZZZZZZZZZZZZZZZZZZZZzgoodZZZZZZZZZZZZZZZZZ
goodZZZZZZZZZZZZZZZZZZbadZZZZZZZZZZZZZZZZZZZZzbadZZZZZZZZZZZZZZZZZ
goodZZZZZZZZZZZZZZZZZZgoodZZZZZZZZZZZZZZZZZZZZzbadZZZZZZZZZZZZZZZZZ
debasish@debasish:~$ 

I want it to replace one "bad" string at a time..Can you please suggest

Thanks,

I think you may have misunderstood my suggestion. re.sub can be called with a function (or method or other callable object) as an argument. That function will be called with a match object as its argument for every occurrence of the pattern.

So you can define a function (or method) that keeps track of a counter to decide whether to perform the replacement or not. Then you can call re.sub with that function as its argument. No loop necessary.

I'm not sure whether that's the simplest solution to your problem, but it's the first that I thought of.

The only other simplish alternative that I can think of would be to loop over all indices in the string, check whether the pattern occurs at that index, increase the counter if so and add the replacement string to the result string and skip a number of character equal to the replacement's string's length if the counter is equal to your n - otherwise add the character at the current index to the result string (which is probably best represented as a list of strings that is joined after the loop). The version using re.sub is probably easier to implement though.

Another approach:

''' re_search_replace1.py
replace second 'bad' with 'good'
'''

import re

s = 'bad1234567bad12345678bad123456789'

print(s)  # test

m = re.search('bad', s)

# apply slicing
s1 = s[:m.end()]
s2 = s[m.end():]

print(s1)  # test
print(s2)  # test

s3 = s2.replace('bad', 'good', 1)

print(s3)  # test
print(s1+s3)

'''
bad1234567bad12345678bad123456789
bad
1234567bad12345678bad123456789
1234567good12345678bad123456789
bad1234567good12345678bad123456789
'''
commented: That's nice. And simpler than my ideas. +5
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.