Quantcast

replace nth occurrence on each line like sed

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

replace nth occurrence on each line like sed

anoopkumar

I assumed vim is directly compatible with sed when you use the :s
substitute commands.

What I am trying to do is to replace the nth occurrence of  a
character with something else.

In sed, you could do: s/c/char/3

So in a set of lines like this

abc abc abcd
c c c c
abcd abc

it would become - after substitution:

abc abc abchard
c c char c
abcd abc

Seems like it doesnt work - when I do that in VIM, I get the first
occurence in the 3rd line replaced to become:

abc abc abcd
c c c c
abchard abc

Am I doing something wrong or is VIM not like sed at all?

Thanks



--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_use" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: replace nth occurrence on each line like sed

Andy Wokula

anokun7 schrieb:

> I assumed vim is directly compatible with sed when you use the :s
> substitute commands.
>
> What I am trying to do is to replace the nth occurrence of  a
> character with something else.
>
> In sed, you could do: s/c/char/3
>
> So in a set of lines like this
>
> abc abc abcd
> c c c c
> abcd abc
>
> it would become - after substitution:
>
> abc abc abchard
> c c char c
> abcd abc
>
> Seems like it doesnt work - when I do that in VIM, I get the first
> occurence in the 3rd line replaced to become:
>
> abc abc abcd
> c c c c
> abchard abc
>
> Am I doing something wrong or is VIM not like sed at all?
>
> Thanks

I've also missed this feature several times.

It is mentioned in the todo list:
    :h todo

-   ":s/pat/foo/3": find 3rd match of "pat", like sed. (Thomas Koehler)

8   Add an argument after ":s/pat/str/" for a range of matches.  For example,
    ":s/pat/str/#3-4" to replace only the third and fourth "pat" in a line.

-   Add number option to ":s//2": replace second occurrence of string?  Or:
    :s///N substitutes N times.

--
Andy

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_use" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: replace nth occurrence on each line like sed

Tony Mechelynck
In reply to this post by anoopkumar

anokun7 wrote:

> I assumed vim is directly compatible with sed when you use the :s
> substitute commands.
>
> What I am trying to do is to replace the nth occurrence of  a
> character with something else.
>
> In sed, you could do: s/c/char/3
>
> So in a set of lines like this
>
> abc abc abcd
> c c c c
> abcd abc
>
> it would become - after substitution:
>
> abc abc abchard
> c c char c
> abcd abc
>
> Seems like it doesnt work - when I do that in VIM, I get the first
> occurence in the 3rd line replaced to become:
>
> abc abc abcd
> c c c c
> abchard abc
>
> Am I doing something wrong or is VIM not like sed at all?
>
> Thanks

Vim substitute is no more identical to sed, than Vim regexps are identical to
perl regexps. They may be similar, but Vim doesn't try to behave identically
as any other program (except Vi when in 'compatible' mode, and even then there
are documented differences). To learn how ":s" works in Vim, you should read
":help :s" and what it resends to, not "man sed". In particular, the syntax of
the ":s" command is defined as

        :[range]s[ubstitute]/{pattern}/{string}/[flags] [count]

Under ":help :s_flags", no numeric flags are mentioned. I suppose your 3 is
understood as a count, i.e., replace in three lines, as mentioned under ":help
:s". The expression "replace in three lines" might be ambiguous; it may depend
on the presence or absence of the "g" flag. Without it, only the first
occurrence on its line is replaced. To replace the 3rd occurrence on all
lines, you could for instance use (untested)

        :1,$s/^[^c]*c[^c]*c[^c]*\zsc/char

Use a different range to act on only part of the file.


Best regards,
Tony.
--
hundred-and-one symptoms of being an internet addict:
234. You started college as a chemistry major, and walk out four years
      later as an Internet provider.

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_use" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: replace nth occurrence on each line like sed

Andreas Politz
In reply to this post by anoopkumar

anokun7 wrote:

>I assumed vim is directly compatible with sed when you use the :s
>substitute commands.
>
>What I am trying to do is to replace the nth occurrence of  a
>character with something else.
>
>In sed, you could do: s/c/char/3
>
>So in a set of lines like this
>
>abc abc abcd
>c c c c
>abcd abc
>
>it would become - after substitution:
>
>abc abc abchard
>c c char c
>abcd abc
>
>Seems like it doesnt work - when I do that in VIM, I get the first
>occurence in the 3rd line replaced to become:
>
>abc abc abcd
>c c c c
>abchard abc
>
>Am I doing something wrong or is VIM not like sed at all?
>
>Thanks
>
>
>
>>
>  
>
:call feedkeys('nnyq') | s/c/char/gc

What's the problem ? ;-)

Putting in it in a command :

com! -nargs=1 -range SedS <line1>,<line2>call SedS(<q-args>)

"%SedS/b/a/2

func! SedS( s_expr ) range
  if a:s_expr !~ '\d\+$'
    exec a:firstline.','.a:lastline.'s'.a:s_expr
  else
    let nth = matchstr(a:s_expr,'\d\+$')+0
    let fk_expr = repeat('n',nth-1)."yq"
    let s_expr = strpart(a:s_expr,0,strlen(a:s_expr)-strlen(nth))
    for line in range(a:firstline,a:lastline)
      exe 'call feedkeys("'.fk_expr.'") | '.line.'s'. s_expr .'gce'
      call inputsave()
    endfor
  endif
endfun


Note: That's just a proof of concept, don't ask me where all the saved
keystrokes endup ( inputsave() ).

-ap





--
Ich hab geträumt, der Krieg wär vorbei.


--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_use" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: replace nth occurrence on each line like sed

Andy Wokula

A.Politz schrieb:

> anokun7 wrote:
>
>> I assumed vim is directly compatible with sed when you use the :s
>> substitute commands.
>>
>> What I am trying to do is to replace the nth occurrence of  a
>> character with something else.
>>
>> In sed, you could do: s/c/char/3
>>
>> So in a set of lines like this
>>
>> abc abc abcd
>> c c c c
>> abcd abc
>>
>> it would become - after substitution:
>>
>> abc abc abchard
>> c c char c
>> abcd abc
>>
>> Seems like it doesnt work - when I do that in VIM, I get the first
>> occurence in the 3rd line replaced to become:
>>
>> abc abc abcd
>> c c c c
>> abchard abc
>>
>> Am I doing something wrong or is VIM not like sed at all?
>>
>> Thanks
>>
> :call feedkeys('nnyq') | s/c/char/gc
>
> What's the problem ? ;-)
>
> Putting in it in a command :
>
> com! -nargs=1 -range SedS <line1>,<line2>call SedS(<q-args>)
>
> "%SedS/b/a/2
>
> func! SedS( s_expr ) range
>   if a:s_expr !~ '\d\+$'
>     exec a:firstline.','.a:lastline.'s'.a:s_expr
>   else
>     let nth = matchstr(a:s_expr,'\d\+$')+0
>     let fk_expr = repeat('n',nth-1)."yq"
>     let s_expr = strpart(a:s_expr,0,strlen(a:s_expr)-strlen(nth))
>     for line in range(a:firstline,a:lastline)
>       exe 'call feedkeys("'.fk_expr.'") | '.line.'s'. s_expr .'gce'
>       call inputsave()
>     endfor
>   endif
> endfun
>
>
> Note: That's just a proof of concept, don't ask me where all the saved
> keystrokes endup ( inputsave() ).
>
> -ap

Nice idea.  For the few occasions where this is needed, it's a really cool
workaround.  Here with some improvements:

- added :sil-ence
- uses input() instead of inputsave() -- no risk of mem overflow
- prints the number of changes, like the original :s would do
- untested: the surrounding inputsave()/inputrestore() pair will be needed
  when used within a mapping

com! -nargs=1 -range SedS <line1>,<line2>call SedS(<q-args>)

"%SedS/b/a/2

func! SedS( s_expr ) range
    if a:s_expr !~ '\d\+$'
        exec a:firstline.','.a:lastline.'s'.a:s_expr
        return
    endif
    call inputsave()
    let nth = matchstr(a:s_expr,'\d\+$')+0
    let fk_expr = repeat('n',nth-1)."yq\r"
    let s_expr = strpart(a:s_expr,0,strlen(a:s_expr)-strlen(nth))
    let nchanges = 0
    for line in range(a:firstline,a:lastline)
        exe 'call feedkeys("'.fk_expr.'") | sil' line.'s'. s_expr .'gce'
        sil let nchanges += input("") !~ "y"
    endfor
    if nchanges > &report
        echo nchanges "substitutions on" nchanges "lines"
    endif
    call inputrestore()
endfunc

--
Andy

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_use" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: replace nth occurrence on each line like sed

Andreas Politz

Andy Wokula wrote:

>A.Politz schrieb:
>  
>
>>anokun7 wrote:
>>
>>    
>>
>>>In sed, you could do: s/c/char/3
>>>
>>>      
>>>
>>:call feedkeys('nnyq') | s/c/char/gc
>>
>>What's the problem ? ;-)
>>
>>
>>    
>>
>
>Nice idea.  For the few occasions where this is needed, it's a really cool
>workaround.  Here with some improvements:
>
>- added :sil-ence
>- uses input() instead of inputsave() -- no risk of mem overflow
>- prints the number of changes, like the original :s would do
>- untested: the surrounding inputsave()/inputrestore() pair will be needed
>  when used within a mapping
>  
>
  - avoid redraws per lazyredraw


com! -nargs=1 -range SedS <line1>,<line2>call SedS(<q-args>)

fun! SedS( s_expr ) range
  if a:s_expr !~ '\d\+$'
    exec a:firstline.','.a:lastline.'s'.a:s_expr
    return
  endif
  let lz_save = &lz
  set lz
  call inputsave()
  let nth = matchstr(a:s_expr,'\d\+$')+0
  let fk_expr = repeat('n',nth-1)."yq\r"
  let s_expr = strpart(a:s_expr,0,strlen(a:s_expr)-strlen(nth))
  let nchanges = 0
  for line in range(a:firstline,a:lastline)
    exe 'call feedkeys("'.fk_expr.'") | sil' line.'s'. s_expr .'gce'
    sil let nchanges += input("") !~ "y"
  endfor
  if nchanges > &report
    echo nchanges "substitutions on" nchanges "lines"
  endif
  call inputrestore()
  let &lz = lz_save
endfun


-ap

--
Ich hab geträumt, der Krieg wär vorbei.


--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_use" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: replace nth occurrence on each line like sed

Christian Brabandt
In reply to this post by anoopkumar

Hi anokun7!

On Sun, 13 Jan 2008, anokun7 wrote:

> What I am trying to do is to replace the nth occurrence of  a
> character with something else.
>
> In sed, you could do: s/c/char/3
>
> So in a set of lines like this
>
> abc abc abcd
> c c c c
> abcd abc
>
> it would become - after substitution:
>
> abc abc abchard
> c c char c
> abcd abc
>
> Seems like it doesnt work - when I do that in VIM, I get the first
> occurence in the 3rd line replaced to become:
>
> abc abc abcd
> c c c c
> abchard abc
>
> Am I doing something wrong or is VIM not like sed at all?

Why don't you filter your lines through sed?


regards,
Christian
--
:wq!

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_use" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Loading...