1

I have a text file with this format...

011000 001i 1111
011001 001i 0001
011010 001i 1111
011011 001i 1011
011100 001i 1111
011101 001i 1111
011110 001i 1001
011111 001i 1111
100000 001i 1111
100001 001i 1110
100001 000i 1000

Columns 1 and 3 are binary values which I would like to convert to hexadecimal (e.g. 011000 becomes 0x18, 1011 becomes 0xB).

This file will eventually be a C language header file (*.h) and the C language doesn't accept binary literals as far as I know without messing about with macros.

If I use Ctrl+v and then use the arrow keys to highlight a column of binary values, is there a set of Vim commands that can automatically go through and convert them all to hexadecimal?

The file isn't so large that I can't do it by hand but I'd love to know if there's a neat way to do this.

EDIT: If possible, solutions for Vim version 7.4.160 or earlier would be preferred but of course not mandatory. :)

3 Answers 3

1

Something like this should work:

%s/\(\d\+\)\s\+\(\S\+\)\s\+\(\d\+\)/\=printf("0x%X %s 0x%02X", str2nr(submatch(1),2), submatch(2), str2nr(submatch(3),2))

It uses a :s command, captures each column and then converts column 1 and 3 to hexadecimal by using a \= sub-replace-expression and the functions str2nr() and printf() are evaluated for each match.

5
  • How do I invoke this? Pasting this after a ':' in command mode seems not to work.
    – Wossname
    Commented Jun 25, 2017 at 15:00
  • Yes, that would be one possibility. Does the regex not match? I took your example data and it worked with that sample. Commented Jun 25, 2017 at 15:23
  • I'm getting a bunch of "E474: Invalid argument" complaints from Vim. I'm using version 7.4.160, if that makes any difference.
    – Wossname
    Commented Jun 25, 2017 at 15:46
  • Looks like str2nr() doesn't like binary as input radix?
    – Wossname
    Commented Jun 25, 2017 at 15:49
  • Yeah, I think this was updated in one of the later patches. Try a newer Vim Commented Jun 25, 2017 at 16:34
1

While this is certainly possible with Vim(Script), I would personally write a small external script for this. It's just easier. My poison of choice for this is usually Python:

#!/usr/bin/env python
import sys

while True:
    line = sys.stdin.readline()
    if line == '':
        break
    l = line.split(' ')
    print('{} {} {}'.format(hex(int(l[0], 2)),
                            l[1],
                            hex(int(l[2], 2))))

We can use:[range]!command because this script reads from stdin; for example for the entire buffer:

:%!./a.py

Or for the visual selection (just select and press :):

:'<,'>!./a.py

You can use Vim's python interface if you don't want to use an external script. I personally like external scripts because they don't require a special API and are easy to write, debug, and reuse ("Rule of Composition").

1

My reputation is too low to comment on Christian's answer, so I modified his answer:

%s/\(\d\+\)\s\+\(\S\+\)\s\+\(\d\+\)/\=printf("0x%X %s 0x%02X", str2nr('0b' . submatch(1)), submatch(2), str2nr('0b' . submatch(3)))

This version does not use the base parameter to str2nr(), but puts a '0b' prefix in front of the binary values.

1
  • Ah yes, forgot about this obvious solution. Commented Jun 27, 2017 at 10:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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