How to write zero-width assertions in vim? - vim

Here is the raw material I’m working with:
line1=a1 abc
line2=abc
line3=aba
line4=cbc
i want to match lines which do not contain character string of "abc" ,the result is :
line3=aba
line4=cbc
how can i get it in vim? maybe the expression is something such as (?!abc) in perl ,i am not sure how to write the regular expression in vim.

To match lines not ending with abc you could write the expression in two ways. My preferred is With very magic
/\v.*(abc)#!/
And with no very magic:
/.*\(abc\)\#!/
I recommend you to take some time to read:
:help magic

From Power Of G:
Delete all lines that do not match a pattern.
:g!/<pattern>/d
Of course, you can replace the d at the end to do something other than deleting the line...

It seems you're familiar with Perl regular expressions. You will probably be interested in :help perl-patterns where you can Vim equivalents for common Perl regex patterns. There, you can see that for a zero-width negative look-ahead, you want \#!.
For other zero-width patterns, including some not listed at :help perl-patterns, see :help /\#= and following. Also useful are \zs and \ze which can avoid some more complex zero-width matches in many cases.

Related

vim multiple character substitute regex issue

I am little new to Vim world. I am trying to substitute *=, ~=(actually [special char]=) in to [whatever is symbol]=(adding space both sides). Here is my substitute command:
:%s/[~,\*]=/ = /g
the problem in this case is that I am not able to add respective special symbol before the equal sign. Can you help me...
This is a classic capture and replace use case. Capture the symbol part by enclosing it in \(...\), and then reference it in the replacement part via \1. You'll find more details at :help s/\1 (or :help :substitute in general):
:%s/\([~,\*]\)=/ \1= /g
Alternatively, you can start the match only on the = with \zs. This asserts that the symbol part is there, but as it isn't included in the match, you don't need to reference it:
:%s/[~,\*]\zs=/ = /g
The same trick can be applied with \ze at the end. As you can see, this often results in shorter commands.
This is probably the simplest answer to your question:
:%s/[~,\*]=/ & /
An& in the replace segment means 'entire match'.

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?
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.
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

replacing part of regex matches

I have several functions that start with get_ in my code:
get_num(...) , get_str(...)
I want to change them to get_*_struct(...).
Can I somehow match the get_* regex and then replace according to the pattern so that:
get_num(...) becomes get_num_struct(...),
get_str(...) becomes get_str_struct(...)
Can you also explain some logic behind it, because the theoretical regex aren't like the ones used in UNIX (or vi, are they different?) and I'm always struggling to figure them out.
This has to be done in the vi editor as this is main work tool.
Thanks!
To transform get_num(...) to get_num_struct(...), you need to capture the correct text in the input. And, you can't put the parentheses in the regular expression because you may need to match pointers to functions too, as in &get_distance, and uses in comments. However, and this depends partially on the fact that you are using vim and partially on how you need to keep the entire input together, I have checked that this works:
%s/get_\w\+/&_struct/g
On every line, find every expression starting with get_ and continuing with at least one letter, number, or underscore, and replace it with the entire matched string followed by _struct.
Darn it; I shouldn't answer these things on spec. Note that other regex engines might use \& instead of &. This depends on having magic set, which is default in vim.
For an alternate way to do it:
%s/get_\(\w*\)(/get_\1_struct(/g
What this does:
\w matches to any "word character"; \w* matches 0 or more word characters.
\(...\) tells vim to remember whatever matches .... So, \(w*\) means "match any number of word characters, and remember what you matched. You can then access it in the replacement with \1 (or \2 for the second, etc.)
So, the overall pattern get_\(\w*\)( looks for get_, followed by any number of word chars, followed by (.
The replacement then just does exactly what you want.
(Sorry if that was too verbose - not sure how comfortable you are with vim regex.)

How to repeat a substitution the number of times the search word occurs in a row in a substitution command in Vim?

I would like to use tabs in a code that doesn’t use them. What I did until now to implement tabs was pretty handcrafty:
:%s/^ /\t/g
:%s/^\t /\t\t/g
. . .
Question: Is there a way to replace two spaces ( ) by tab (\t) the number of times it was found at the beginning of a line?
There are (at least) three substitution techniques relevant to this case.
1. The first one takes advantage of the preceding-atom matching
syntax to naturally define a step of indentation. According to the
question statement, an indent step is a pair of adjacent space
characters preceded with nothing but spaces from the beginning
of line. Following this definition, one can construct the actual
substitution pattern, right to left:
:%s/\%(^ *\)\#<= /\t/g
Indeed, the pattern designates an occurrence of two literal space
characters, but only when they are preceded by a zero-width match
of the atom just before \#<=, which is the pattern ^ * wrapped in
grouping parentheses \%(, \). These non-capturing parentheses are
used instead of the usual capturing ones, \(, \), since there is no
need in further referring to the matched string of leading spaces. Due
to the g flag, the above :substitute command runs through the
leading spaces pair by pair, and replaces each of them by single tab
character.
2. The second technique takes a different approach. Instead of
matching separate indent levels, one can break each of the lines
starting with space characters down into two lines: one containing
the indenting spaces of the original line, and another holding the
rest of it. After that, it is straightforward to replace all of the pairs
of spaces on the first line, and concatenate the lines back together:
:g/^ /s/^ \+/&\r/|-s/ /\t/g|j!
3. The third idea is to process leading spaces by means of Vim
scripting language. A convenient way of doing that is to use the
substitute with an expression feature of the :substitute command
(see :help sub-replace-\=). When started with \=, the substitute
string of the command enables to substitute the matches of a pattern
with results of evaluation of the expression specified after \=:
:%s#^ \+#\=repeat("\t",len(submatch(0))/2)
If you specifically want to convert spaces into tabs (or vice-versa) at the start of a line, there's the useful :retab command which takes care of that. For example:
:retab! 2 will convert spaces in groups of two to tabs
:set expandtab and then :retab! 2 will convert tabstops (of width 2) back to spaces
See :h :retab (and :h 'ts') for the details.
This is not a general solution for the original problem, but I think it covers the most common use case.
There is no general way of doing this using :s regex's. You can't make the /g modifier look backwards otherwise it'd be unusable, and you can't reliably check that you're at the beginning of the line without looking backwards.
The only way of doing it generally is to loop, like so:
:for i in range(100)
: %s/^\t*\zs /\t/e
:endfor
Which is ugly, slow and highly unrecommended. Use :retab

Vim Search/replace: what do I need to escape?

I'm trying to search and replace $data['user'] for $data['sessionUser'].
However, no matter what search string I use, I always get a "pattern not found" as the result of it.
So, what would be the correct search string? Do I need to escape any of these characters?
:%s/$data['user']/$data['sessionUser']/g
:%s/\$data\[\'user\'\]/$data['sessionUser']/g
I did not test this, but I guess it should work.
Here's a list of all special search characters you need to escape in Vim: `^$.*[~)+/
There's nothing wrong with with the answers given, but you can do this:
:%s/$data\['\zsuser\ze']/sessionUser/g
\zs and \ze can be used to delimit the part of the match that is affected by the replacement.
You don't need to escape the $ since it's the at the start of the pattern and can't match an EOL here. And you don't need to escape the ] since it doesn't have a matching starting [. However there's certainly no harm in escaping these characters if you can't remember all the rules. See :help pattern.txt for the full details, but don't try to digest it all in one go!
If you want to get fancy, you can do:
:%s/$data\['\zsuser\ze']/session\u&/g
& refers to the entire matched text (delimited by \zs and \ze if present), so it becomes 'user' in this case. The \u when used in a replacement string makes the next character upper-case. I hope this helps.
Search and replace in vim is almost identical to sed, so use the same escapes as you would with that:
:%s/\$data\['user'\]/$data['session']/g
Note that you only really need to escape special characters in the search part (the part between the first set of //s). The only character you need to escape in the replace part is the escape character \ itself (which you're not using here).
The [ char has a meaning in regex. It stands for character ranges. The $ char has a meaning too. It stands for end-line anchor. So you have to escape a lot of things. I suggest you to try a little plugin like this or this one and use a visual search.

Resources