5

I want to search for:

From start of the line, any number of white spaces followed by any number of digits NOT followed by a dot.

And replace it with all that was searched and a dot. This is just to introduce a dot in a simple numbered list when forgotten.

So, I went:

:%s/^\(\s*\d\+\)\.\@!/\1\.

This breaks down if there is a two digit number with a dot following it.

For example:

  • This 1 Some text becomes this 1. Some text as expected.

  • But, this 10. Some text becomes 1.0. Some text instead staying unchanged.

  • this works for me with a 7.4.1689 and 8.0.586 What version have you been using? – Christian Brabandt Aug 2 '17 at 7:56
  • @ChristianBrabandt I am using vim 8.0 and it works like I have explained in the question. – deshmukh Aug 3 '17 at 5:20
1

You're almost there, what you need is \> at the end of the regular expression like so:

:%s/^\(\s*\d\+\)\.\@!\>/\1\.

Or even better:

:%s/^\s*\d\+\.\@!\>\zs/.

Note: A . in the replacement part of the :substitute command is literal so you can remove the leading \. :help sub-replace-special describes what needs to be escaped and its meaning.

But if you want to make it work for:

10.10 something
10.10.10 something
10.10.10.10 something

and so on, you'll need a regular expression that's similar to yours:

:%s/^\s*[0-9.]*\d\.\@!\>\zs/.

Explanation

  • ^\s* Whitespaces at the beginning of the line.
  • [0-9.]* Digits and literal dots that match the correct part of the string, i.e., the 10.1, 10.10.1 and 10.10.10.1 in the previous example.
  • \d\.\@! The digit that requires a dot after it.
  • \> The previous digit at the end of a word (see :help /\>).
  • \zs\ If the previous regular expression matches, ignore it and match what follows, i.e., just after the end of the word and before the whitespace in the previous example (see :help /\zs).
  • nice explanation and told me what was wrong with my solution! \>!! – deshmukh Aug 6 '17 at 10:01
6

I'm surprised how complicated this can be done (-;

Your requirement can be met with a simple

:%s/^\s*\d\+\zs\.*/./

Explanation:

^       Lines starting with
\s*   any number of blanks
\d\+ followed by one or more digits
\zs   here starts the pattern that will get replaced
\.*   any number of dots (you could also do \.\?) gets replaced
/./   by a single dot

So the trick is to always put a dot behind the number. If there already is a dot, it gets replaced by a new one.

  • This works perfectly. I almost understand how. It replaces all "Start Space Digits Character" sequence with "Start Space Digits Dot". But I am not clear about the exact syntax used. If you could please explain, that would be great – deshmukh Aug 3 '17 at 5:19
  • 1
    You are using things like \@! I never used so I thought you don't need an explanation. (-: Now I added one. – Philippos Aug 3 '17 at 6:13
4

If I understand correctly your question what you want is this:

:%s/\s*\d\+\([0-9\.]\)\@!/\0.

You need to group the digits and the dot in the group you're excluding with \@! other wise the match stops before the 0 in 10 and the dot is inserted whereas it shouldn't.

With this command you transform this:

1 Foo
10 Bar
10. Bar
    1 Foo
    10 Bar
    10. Bar

in this:

1. Foo
10. Bar
10. Bar
    1. Foo
    10. Bar
    10. Bar
3

Instead of using a zero-width match to specify not to include the dots, you could set the end of the match and then specify what is included in the "wrong" lines. That is, if the digits are followed by space or end-of-line, one should add a dot:

:%s/^\s*\d\+\ze\(\s\|$\)/\0.

Here \ze sets the end of the match, and the group matches either a space or the end-of-line.

3

This seems to work:

%s/^\(\s*\d\+\)\(\.\?\d\+\)\?\.\?\(\s\)/\1\2.\3/g
  • the first group is any number of "empty" characters followed by one or more digits;
  • the second group is zero or one point followed by one ore more digits, but this group can appear one or more times;
  • the third group is any number of "empty" characters at the end (so they can be preserved);

It transforms this:

  1. abobora
  10. abobora
  1 abobora
  10 abobora
  10.5 abobora

1 Foo
10 Bar
10. Bar
    1 Foo
    10 Bar
    10. Bar

into this:

  1. abobora
  10. abobora
  1. abobora
  10. abobora
  10.5. abobora

1. Foo
10. Bar
10. Bar
    1. Foo
    10. Bar
    10. Bar

It would be easier to understand without all those escape characters:

    %s/^(\s*\d+)(\.?\d+)?\.?(\s)/\1\2.\3/g

But without them it wouldn't work :)

  • You can use \v to turn on very magic magic which would make your substitution work, :%s/\v^(\s*\d+)(\.?\d+)?\.?(\s)/\1\2.\3/. However we can do even better by using \zs to get rid of all the capture groups, %s/\v^\s*\d+(\.?\d+)?\zs\.?/./ – Peter Rincker Aug 2 '17 at 17:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.