Scott
Scott

Reputation: 12356

Find and replace only part of a single line in Vim

Most substitution commands in vim perform an action on a full line or a set of lines, but I would like to only do this on part of a line (either from the cursor to end of the line or between set marks).

example

this_is_a_sentence_that_has_underscores = this_is_a_sentence_that_should_not_have_underscores

into

this_is_a_sentence_that_has_underscores = this is a sentence that should not have underscores

This task is very easy to do for the whole line :s/_/ /g, but seems to be much more difficult to only perform the replacement for anything after the =.

Can :substitution perform an action on half of a line?

Upvotes: 12

Views: 4443

Answers (2)

Peter Rincker
Peter Rincker

Reputation: 45087

Look-around

There is a big clue your post already:

only perform the replacement for anything after the =.

This often means using a positive look-behind, \@<=.

:%s/\(=.*\)\@<=_/ /g

This means match all _ that are after the following pattern =.*. Since all look-arounds (look-aheads and look-behinds) are zero width they do not take up space in the match and the replacement is simple.

Note: This is equivalent to (?<=...) in perl speak. See :h perl-patterns.

What about \zs?

\zs will set the start of a match at a certain point. On the face this sounds exactly what is needed. However \zs will not work correctly as it matches the pattern before the \zs first then the following pattern. This means there will only be one match. Look-behinds on the other hand match the part after \@<= then "look behind" to make sure the match is valid which makes it great for multiple replacement scenario.

It should be noted that if you can use \zs not only is it easy to type but it is also more efficient.

Note: \zs is like \K in perl speak.

More ways?!?

As @glts mentioned you can use other zero-width atoms to basically "anchor" your pattern. A list of a few common ways:

  • \%>a - after the 'a mark
  • \%V - match inside the visual area
  • \%>42c - match after column 42

The possible downside of using one of these methods they need you to set marks or count columns. There is nothing wrong with this but it means the substitution will maybe affected by side-effects so repeating the substitution may not work correctly.

For more help see:

:h /\@<=
:h /zero-width
:h perl-patterns
:h /\zs

Upvotes: 8

glts
glts

Reputation: 22674

Two solutions I can think of.

Option one, use the before/after column match atoms \%>123c and \%<456c.

In your example, the following command substitutes underscores only in the second word, between columns 42 and 94:

:s/\%>42c_\%<94c/ /g

Option two, use the Visual area match atom \%V.

In your example, Visual-select the second long word, leave Visual mode, then execute the following substitution:

:s/\%V_/ /g

These regular expression atoms are documented at :h /\%c and :h /\%V respectively.

Upvotes: 14

Related Questions