user1773603
user1773603

Reputation:

How to replace the Nth occurence of a pattern to add extra terms in vim

I have:

elif paramo == 3:
    fold_path = [Pk_Path+"ns_up", Pk_Path+"ns_up2", Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw2"]
    CC_path = ["C_ns_up", "C_ns_up2", "C_fid", "C_ns_dw", "C_ns_dw2"] 

and I would like to get:

elif paramo == 3:
    fold_path = [Pk_Path+"ns_up", Pk_Path+"ns_up2", Pk_Path+"ns_up3", Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw2", Pk_Path+"ns_dw3"]
    CC_path = ["C_ns_up", "C_ns_up2", "C_ns_up3", "C_fid", "C_ns_dw", "C_ns_dw2", "C_ns_dw3"] 

For the first line I can add an extra Pk_Path+"ns_up3" after Pk_Path+"ns_up2" by doing:

:'<,'>s/, Pk_Path+\(.\{-}\)_up2"/, Pk_Path+\1_up2", Pk_Path+\1_up3"/g

and it insertions "ns_up3":

elif paramo == 3:
    fold_path = [Pk_Path+"ns_up, "Pk_Path+"ns_up2", Pk_Path+"ns_up3", Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw2"]
    CC_path = ["C_ns_up", "C_ns_up2", "C_fid", "C_ns_dw", "C_ns_dw2"] 

But for the other term Pk_Path+"ns_dw3", I tried to do:

:'<,'>s/, Pk_Path+\(.\{-}\)_dw2"/Pk_Path+\1_dw2", Pk_Path+\1_dw3"/g

but this produced the wrong results:

elif paramo == 3:
    fold_path = [Pk_Path+"ns_up"Pk_Path+"ns_up2"Pk_Path+"ns_up3", Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw2", Pk_Path+"ns_up3", Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw3"]
    CC_path = ["C_ns_up", "C_ns_up2", "C_fid", "C_ns_dw", "C_ns_dw2"]

I don't know how to explicitly indicate that I want to insert Pk_Path+"ns_dw3" after the sixth Pk_Path string.

I tested the following command with \zs, I don't understand why this doesn't work:

:'<,'>s/\(\zsPk_Path\)\{6}+"\(.\{-}\)_dw2"/Pk_Path+\2_dw3"/g

It seems that I take the sixth occurrence of Pk_Path with ns_dw2, then add the \2 pattern and the ns_dw3 string.

Update 1

I am close to the solution. For only the line:

fold_path = [Pk_Path+"ns_up", Pk_Path+"ns_up2", Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw2"]

If I want to insert simultaneously Pk_Path+"ns_up3" after Pk_Path+"ns_up2" and Pk_Path+"ns_dw3" after Pk_Path+"ns_dw2", I think following may be a solution closed to the wanted result:

(under vim), by doing:

%s/\(Pk_Path+".\{-}_\)\([a-z]\{2}\)2"/\1\22,\1\23"/g

i.e, I get the first pattern \1 corresponding to Pk_Path+"ns_and second pattern which represents upor dw and I add to it the number 2. So Finally, I replace them by for example the current Pk_Path+"ns_up2" by Pk_Path+"ns_up2" plus Pk_Path+"ns_up2",Pk_Path+"ns_up3" (I was doing this by the part : \1\22",\1\23"

but unfortunately, I obtain:

fold_path = [Pk_Path+"ns_up", Pk_Path+"ns_up",Pk_Path+"ns_up", Pk_Path+"ns_up3", Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw",Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw3"]

As you can see, Pk_Path+"ns_up2" has disappeared and Pk_Path+"ns_up" like Pk_Path+"fid" appears 2 times.

Is it possible to use \1\22, \1\23 in order to insert \1\23 after \1\22. In order words, I would like to insert \1\23 after \1\22.

Where is my mistake?

Update 2

Here's the right command that I have been searching from the original post:

  1. Expression to process (input) :

    fold_path = [Pk_Path+"ns_up", Pk_Path+"ns_up2", Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw2"]

  2. Command applied :

    :%s/(Pk_Path+"[a-z]{2}_[a-z]{2})(2)"/\1\2, \13"/g

  3. Result (output) :

fold_path = [Pk_Path+"ns_up", Pk_Path+"ns_up2, Pk_Path+"ns_up3", Pk_Path+"fid", Pk_Path+"ns_dw", Pk_Path+"ns_dw2, Pk_Path+"ns_dw3"]

Upvotes: 3

Views: 156

Answers (2)

B.G.
B.G.

Reputation: 6026

Well I am not sure if this is what you are searching but here is my try:

:%s/\(Pk_Path+".\{-}",\=\s\=\)\{5}\zs\ze.*/, Pk_Path+"ns_dw3"/g 

But if it is this that you are trying to achieve a simple search and then using 5n (in a macro or so) may be an easier solution. A replace may not be the best way to insert new text.

Update: I really am unsure what you want to achieve but to just solve the case you put in your update, it is a rather simple case of a regex:

%s/\(.*Pk_Path+"ns_up2"\)\(.*Pk_Path+"ns_dw2"\)\(.*\)/\1, Pk_Path+"ns_up3"\2, Pk_Path+"ns_dw3"\3

Breakdown:

\(.*Pk_Path+"ns_up2"\) " Capture everything up to "ns_up2" into group 1 
\(.*Pk_Path+"ns_dw2"\) " Capture from "ns_up2" to "ns_dw2" into group 2
\(.*\)                 " Capture the rest into group 3

Then it is just a matter of inserting your strings between group 1 and 2 and between group 2 and 3

Upvotes: 0

Ingo Karkat
Ingo Karkat

Reputation: 172570

The :substitute command has an optional [count] argument; unfortunately, it just limits replacement to a number of lines, and does not count matches.

The only built-in option is supplying the confirm flag, and answer the prompts with nnnnnl to just replace the 6th match.

Alternatively, you can implement the counting inside the pattern, by matching the preceding elements with a \%(...\)\{5}, and then starting the actual match with :help /\zs. But that's really cumbersome to write, and you need to duplicate the pattern (e.g. \%(foo\)\{5}\zsfoo).

My PatternsOnText plugin has (among others) a :SubstituteSelected variant where you can directly specify the count (as 2,5 or 3-5 or even repeating y3nyyn answers).

Upvotes: 2

Related Questions