Various ways of substitution in Vim - vim

How would you do to turn this
Dragon.Ball.026.C'est.la.finale.!!.Kame.hame.ha.mkv
Dragon.Ball.027.Goku...Le.moment.le.plus.critique.mkv
Dragon.Ball.028.Impact.!!.La.puissance.contre.la.puissance.mkv
Dragon.Ball.029.A.nouveau,.l'aventure..Le.lac.errant.mkv
Dragon.Ball.030.Pilaf.et.l'armée.mystérieuse.mkv
into this
Dragon.Ball.026.mkv
Dragon.Ball.027.mkv
Dragon.Ball.028.mkv
Dragon.Ball.029.mkv
Dragon.Ball.030.mkv
I succeeded with difficulty with a macro, but there may be a simple way ?
(substitution, block mode, macro or anything else..)
It's for my personal knowledge,
Regards.

A slightly shorter solution
:%s/\d\.\zs.*\.//
That is, find digit followed by dot; then match and delete everything until (and including) the last dot. The regex is guaranteed to work right as the "star" operator is greedy.

You can remember parts of a match with capture groups: \( and \). So match and
remember everything up to the last digit \d, plus the rest of the line .*
(but don't remember this part), and use \1 to put back the remembered part of
the line, and manually add the extension .mkv:
:%s/\(.*\d\).*/\1.mkv
See :help pattern-searches, :help pattern-atoms and in particular :help \(
Note
You could also remember the extension with a second capture group and add it
back like with \2:
:%s/\(.*\d\).*\(\.mkv\)/\1\2

Related

Select first char up to first non camelCase or non upper case char or up to first snake case _ in vim

I used this map:
map ,w v/\([^ a-z0-9]\|[^ A-Z0-9]\)*<cr>h
the idea is to select
in the words
mysuperTest
MYSUPER_TEST
mysuper_test
to always select the part that says mysuper
but it doesnt work, not sure why
I would use something like the below:
nnoremap ,w v/\C\%#.\([a-z]\+\<bar>[A-Z]\+\)\zs<cr>h
One point to notice is that in a mapping you need to use <bar> (or escape | with an extra backslash) since otherwise | is recognized as a command separator (see :help map-bar.)
Another one to notice is that you want the match to start at the first character outside the word (so you'll land at the end of the word with the h). The visual selection will expand to the start of the match in a search. I suggest using \zs to set the start of the match explicitly (see :help /\zs.)
Finally, beware of 'ignorecase' or 'smartcase' settings. Use \C to explicitly request a case-sensitive match (see :help /\C.)
I also like the idea of using a stronger anchor for the start of the match, so I'm using \%# to match the current cursor position (see :help /\%#), so you're always sure to match the current word only and not end up wandering through the buffer.
Putting it all together:
\C Case-sensitive search
\%# From cursor position
. Skip first character
\( Either one of:
[a-z]\+ One or more lowercase letters
\| (\<bar>) Or:
[A-Z]\+ One or more uppercase letters
\) End group
\zs Set match position here
I'm skipping the first character under the cursor, since in a CamelCase word, the first character won't match capitalization of the remainder of the word.
I kept your original idea of finding the first character after the word then using h to go back one to the left. But that might be a problem if, for example, the word is at the end of the line.
You can actually match the last character of the word instead with something like [a-z]\+\zs[a-z], which will set the start of the match on the last lowercase character. You can do this for both sides of the group (you can have more than one \zs in your pattern, last wins.) If you structure your match that way, you won't need the final h to go back.
I didn't handle numbers, I'll leave those as an exercise to the reader.
Finally, consider there are quite a few corner cases that can make such a mapping quite tricky to get right. Rather than coming up with your own, why not look at plug-ins which add support for handling CamelCase words that have been battle-tested and will cover use cases a lot more advanced than the simple expression you're using here?
There's the excellent vim-scripts/camelcasemotion by Ingo Karkat which sets up a ,w mapping to move to the start of the next CamelCase word, but also i,w to select the current one. You can use powerful combinations such as v3i,w to visually select the current and next two CamelCase words.
You might also check Tim Pope's tpope/vim-abolish which, among other features, defines a set of cr mappings to do coercion from camelCase to MixedCase, snake_case, UPPER_CASE, etc. (Not directly about selecting them, but still related and you might find it useful.)

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

Vim Substitution

I always wanted to know, how you can substitute within given parameters.
If you have a line like this:
123,Hello,World,(I am, here), unknown
and you wnat to replace World with Foobar then this is an easy task: :%s/World/Foobar/
Now I wonder how I can get rid of a , which is wihtin the ( ).
Theoretically I just have to find the first occurance of ( then substitute the , with a blank until ).
Try lookahead and lookbehind assertions:
%s/([^)]*\zs,\ze.*)//
(\zs and \ze tell where pattern starts and end)
or
%s/\(([^)]*\)\#<=,\(.*)\)\#=//
The first one is more readable, the second one uses \( ... \) groupings with parentheses inside groups which makes it look like obfuscated, and \#<= which apart from being a nice ASCII-art duck is the lookbehind operator, and \#= that is the lookahead operator.
References: :help pattern (more detail at :help /\#=, :help /\ze, etc.)
You use the GUI and want to try those commands? Copy them into the clipboard and run :#+ inside Gvim.
Modifying slightly the answer of #Tom can give you a quite good and "a bit" more readable result :
%s/\(.*\)(\(.*\),\(.*\))\(.*\)/\1(\2\3)\4/
That way you will have : in \1 will store what is at the left outside of the parenthesis, \4 what is at the right outside of the parenthesis and \2 and \3 what is inside the parenthesis, respectively on the left (\2) and on the right (\3).
With that you can easily swap your elements if your file is organised as column.
You can also select the text you want to change (either with visual or visual-block modes) and enter the : to start the replace command. vi will automatically start the command with :'<,'> which applies the command to the selected area.
Replacing a , can be done with:
:'<,'>s/,/ /g
For your example, this is the same thing as suggested by #ubuntuguy
%s/\(.*\)(\(.*\),\(.*\)/\1(\2\3
This will do the exact replacement you want.
Yet another approach, based on the fact that actually you want to substitute only the first occurrence of , inside the parenthesis:
:%s#\((.\{-}\),#\1 #
Explanation:
:%s for substitution in the whole file (don't use % if you want to work only with the current line)
we can use # or : as a delimiter to make the command more readable
in (.\{-} we ask to find any symbol (dot) after the left parenthesis and the rest stands for 0 or more occurrence (as few as possible) of this symbol. This expression is put inside \(...\) to be able to refer to this group as \1 in the future.

Resources