Vi and Vim Stack Exchange is a question and answer site for people using the vi and Vim families of text editors. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a large xhtml document that is broken into some 245 long paragraphs, each with a unique id in the form of "ud###"

<p class="windowbg" id="ud000">
   <!--- lots of content --->
</p>
...
<p class="windowbg" id="ud244">
   <!--- and a bit more --->
</p>

Since each 'paragraph' is several times larger than the viewport, i wanted to add links at the top of each to enable jumping around.

Here's what i came up with:

%s/\"ud\(\d\{3}\)\">/\=submatch(0)."<a href =\"#ud".printf("%03d", submatch(1) - 1)."\">prev<\/a> <a href=\"#ud".printf("%03d", submatch(1) + 1)."\">next<\/a>"/

Now, the problem is that it didn't work.

The first 9 replacements were perfect, with the relevant line coming out like this:

<p class="windowbg" id="ud001"><a href="#ud000">prev</a> <a href="#ud002">next</a>

But from 010 to 017, they came out weird, the links off by two

<p class="windowbg" id="ud010"><a href="#ud007">prev</a> <a href="#ud009">next</a>

Then 018 and 019 were perfect, 020 through 027 came out off by four, 028 and 029 were correct, 030 through 037 were off by 6...

I don't know how long the pattern holds, and I can't figure out why it shows up in the first place.

Any ideas? Is this some well known bug?

share|improve this question
up vote 7 down vote accepted

Octal numbers! Numbers starting with 0 are taken to be in octal. So, 00108 = 000810, and subtracting one, we get 000710 = 00078.

See :h octal:

                                                    octal
Conversion from a String to a Number is done by converting the first digits to
a number.  Hexadecimal "0xf9", Octal "017", and Binary "0b10" numbers are
recognized.  If the String doesn't start with digits, the result is zero.
…
To avoid a leading zero to cause octal conversion, or for using a different
base, use str2nr().

So, your substitution replacement will look like:

\=printf("%s <a href='#ud%03d'>prev<\/a> <a href='#ud%03d'>next<\/a>", submatch(0), str2nr(submatch(1)) - 1, str2nr(submatch(1)) + 1)/

Note that you can use a single printf for the whole thing.

str2nr can take a base, but:

When {base} is omitted base 10 is used.  This also means that
a leading zero doesn't cause octal conversion to be used, as
with the default String to Number conversion.
share|improve this answer
    
In hindsight, the source of the problem should have been obvious, what with the pattern I was seeing. I suppose it just never occurred to me that vim might bother to deal with any base other than decimal. That printf statement is amazing too. I have much to learn. – FitzmorrisPR Feb 25 at 8:19
    
Oh. Just a small quibble, in that the printf statement seems to treat the backslashes as literals, which left all the <a> tags unclosed. Easy enough to fix – FitzmorrisPR Feb 25 at 8:47
    
@FitzmorrisPR Oh, yeah, I used single-quoted strings, so I needn't have escaped the /. – muru Feb 25 at 8:48
    
@FitzmorrisPR Guess I was mistaken. I had to use double quotes for the format string, and escape the / before it worked correctly (or use a character other than / to separate :s's patterns). – muru Feb 25 at 8:51

It's because Vim is interpreting the numbers as octal. See :h octal. The suggestion there is to use str2nr() so wrap your submatch calls

%s/\"ud\(\d\{3}\)\">/\=submatch(0)."<a href =\"#ud".printf("%03d", str2nr(submatch(1)) - 1)."\">prev<\/a> <a href=\"#ud".printf("%03d", str2nr(submatch(1)) + 1)."\">next<\/a>"/
                                                                   ^^^^^^^           ^                                                  ^^^^^^^           ^
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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