Submatch question in a substitute

3 views
Skip to first unread message

Jeri Raye

unread,
Jul 9, 2009, 3:35:06 PM7/9/09
to v...@vim.org
Hi,

When I execute this line

:%s/\<[01]\+\>/\=Bin2Hex(submatch(0))/

on this binary tabel

0000
0001
[...]
1111

it is all converted nicely to hexadecimal

However, my binary numbers are prefixed with a % character.
So
%0000
%0001
[...]
%1111

How to I change the substitute command above so that it works again?
I tried
:%s/\<%[01]\+\>/\=Bin2Hex(submatch(1))/
But this one gives an pattern not found error

And
:%s/%\<[01]\+\>/\=Bin2Hex(submatch(1))/
changes everything into zero.
So I assume the submatch position is wrong


Here below the Bin2Hex function is described.
Note: Not developed by my but provided trough this fantastic mailing group
+--------------------------------------------------------------------------------------------------------+
func! Bin2Hex(bin)
let bin = matchstr(a:bin, '[01]\+')
if bin == ""
return "0"
endif
let bin = repeat("0", PadSize(strlen(bin), 4)). bin
let runs = split(bin, '....\zs')
call map(runs, 's:fourbd2hd(v:val)')
return join(runs, '')
endfunc

" return the first run of hex digits in string {hex} converted into
" binary digits (a match at "0x" is skipped)
func! Hex2Bin(hex)
let hex = matchstr(a:hex, '\%(0x\)\@!\x\+')
if hex == ""
return "0"
endif
return join(map(split(hex, '\m'), 's:hd2fourbd(v:val)'), '')
endfunc

" convert 4 bin digits (string) into 1 (lower case) hex digit
func! s:fourbd2hd(bd)
let bd = a:bd
return printf("%x", 8*bd[0] + 4*bd[1] + 2*bd[2] + bd[3])
endfunc

" convert 1 hex digit (string) into 4 bin digits
func! s:hd2fourbd(hd)
let hd = eval("0x". a:hd)
return (hd/8) . (hd/4%2) . (hd/2%2) . (hd%2)
endfunc

" calculate number of pad chars to make {len} a multiple of {chunksize}
func! PadSize(len, chunksize)
return -1 + (a:chunksize - (a:len + a:chunksize - 1) % a:chunksize)
endfunc
" padsize := PadSize(len,chunksize) <=>
" 0 <= padsize < chunksize AND (len + padsize) mod chunksize = 0

" NOTE: must not use substitute() in the functions to avoid recursive
" submatch() when called from :s/.../\=.../
" substitute(bin,'[01]\{4}', '\=s:fourbd2hd(submatch(0))', 'g')
" substitute(a:hex, '\x', '\=s:hd2fourbd(submatch(0))', 'g')
+--------------------------------------------------------------------------------------------------------+

Rgds,
Jeri

Tim Chase

unread,
Jul 9, 2009, 3:59:18 PM7/9/09
to v...@vim.org
> When I execute this line
>
> :%s/\<[01]\+\>/\=Bin2Hex(submatch(0))/
>
> on this binary tabel
>
> 0000
> 0001
> [...]
> 1111
>
> it is all converted nicely to hexadecimal
>
> However, my binary numbers are prefixed with a % character.
>
> How to I change the substitute command above so that it works again?
> I tried
> :%s/\<%[01]\+\>/\=Bin2Hex(submatch(1))/
> But this one gives an pattern not found error
> And
> :%s/%\<[01]\+\>/\=Bin2Hex(submatch(1))/
> changes everything into zero.
> So I assume the submatch position is wrong

The \< won't match on the left of a "%" (unless the "%" is in
your 'isk' setting: ":help 'isk' for more on that"). Move your
"%" to the other side of the "\<" and it should work.
Alternatively, since you've specified the context as requiring a
"%", you can just skip the "\<" altogether.

That takes care of the pattern-not-found problem. However, then
the results of submatch(1) in the replacement now hold "%0001" or
whatever. Vim tries to run this through your Bin2Hex, but
"%0001" isn't a a valid number for parsing, so it chokes. I
noticed you also shifted from submatch(0) [the whole match] to
submatch(1) [something tagged with \(...\) for later
backreferencing]. The final solution could look something like

:%s/%\([01]\+\)\>/\=Bin2Hex(submatch(1))

which captures the stuff after the "%" as submatch(1) but
replaces the whole thing with the output of Bin2Hex().

Hope this helps shed some light on what's going on, as well as
provide you with a solution.

-tim


Reply all
Reply to author
Forward
0 new messages