1

My file contains ISBN-10 and ISBN-13, e.g.:

isbn:0062316095, more text
isbn:1847922635, much more text
isbn:9780486406510, even more text

I want to convert every ISBN-10 to the according ISBN-13 (rules). How can I do that in Vim?

Vim version is MacVim 8.0.525 (130).

4

I believe this does what you want. I've made a function to do the conversion and a mapping that triggers it. Simply place your cursor on the ISBN-10 and press Alt+i to replace it with its corresponding ISBN-13 number. (<A-i> is just a suggestion, and you can, of course, change it to whatever works best for you.)

nnoremap <A-i> ciw<C-r>=ISBN10ToISBN13('<C-r>"')<CR>

function! ISBN10ToISBN13(ISBN10)
   let ISBN13 = ''
   if (strlen(a:ISBN10) == 10)
      " Remove last number
      let ISBN13 = strpart(a:ISBN10, 0, 9)
      " Prepend 978
      let ISBN13 = '978'.ISBN13

      " Calculate the check digit
      let i = 1
      let total = 0
      for digit in split(ISBN13, '\zs')
         let total += str2nr(digit)*i
         if (i == 1)
            let i = 3
         else
            let i = 1
         endif
      endfor
      let check = (10 - (total % 10)) % 10

      " Append the check digit
      let ISBN13 .= check
      return ISBN13
   else
      " Return -1 if input digit is invalid.
      return -1
   endif
endfunction

And finally to replace ALL ISBN-10 in a file you can run a global command like the following:

:g/isbn:\d\{10}\>/ exe "normal!wwciw\<C-r>=ISBN10ToISBN13('\<C-r>\"')\<CR>"
  • :g run a command on every line that matches a pattern.
  • /isbn:\d\{10}\>/ A pattern that matches only ISBN-10 numbers.
  • exe ... Execute the following command (We need this so that we can use special characters like <C-r>)
  • normal!... A normal command that is identical to the <A-i> mapping above.

See :help split(), :help str2nr(), :help c_<C-R>, and :help :global for more info.

2
  • You are awesome.
    – lejonet
    May 2 '17 at 19:10
  • 2
    You could also apply your function to a substitute command, something in the lines of: :%s/isbn:\zs\d\{10\}\>/\=ISBN10ToISBN13(submatch(0))/g. I think that would be slightly better than the global command, for instance because it would work for multiple isbn's on a single line. May 3 '17 at 6:03

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.