5
\$\begingroup\$

I'm given a hexadecimal number in string form with a leading "0x" that may contain 1-8 digits, but I need to pad the number with zeros so that it always has 8 digits (10 characters including the "0x").

For example:

  • "0x123" should become "0x00000123".
  • "0xABCD12" should become "0x00ABCD12".
  • "0x12345678" should be unchanged.

I am guaranteed to never see more than 8 digits, so this case does not need to be handled.

Right now, I have this coded as:

padded = '0x' + '0' * (10 - len(mystring)) + mystring[2:]

It works, but feels ugly and unpythonic. Any suggestions for a cleaner method?

\$\endgroup\$
8
\$\begingroup\$

Perhaps you're looking for the .zfill method on strings. From the docs:

Help on built-in function zfill:

zfill(...)
    S.zfill(width) -> string

    Pad a numeric string S with zeros on the left, to fill a field
    of the specified width.  The string S is never truncated.

Your code can be written as:

def padhexa(s):
    return '0x' + s[2:].zfill(8)

assert '0x00000123' == padhexa('0x123')
assert '0x00ABCD12' == padhexa('0xABCD12')
assert '0x12345678' == padhexa('0x12345678')
\$\endgroup\$
  • \$\begingroup\$ This is exactly what I was looking for. Thanks! \$\endgroup\$ – Sohcahtoa82 Oct 23 '14 at 17:42
3
\$\begingroup\$

I would suggest interpreting the input as a number, then using standard number-formatting routines.

padded = str.format('0x{:08X}', int(mystring, 16))

The string → int → string round trip may seem silly, but it is also beneficial in that it provides validation of the input string.

\$\endgroup\$
0
\$\begingroup\$

I'd use something like this:

def format_word(word, prefix=None, size=None):
    if prefix is None:
        prefix = '0x'
    if size is None:
        size = 2
    if not isinstance(word, int):
        word = int(word, 16)
    if word > 2**(8 * size) - 1:
        raise ValueError('word too great')
    return '{prefix}{word:0{padding}X}'.format(
            prefix=prefix,
            word=word,
            padding=2 * size)

Using None as defaults and assigning true defaults later makes it easier to wrap the function.

def format_word(word, prefix=None, size=None):
    if prefix is None:
        prefix = '0x'
    if size is None:
        size = 2

If word isn't already a int try to convert it. It'll choke if it's not a str, bytes, or bytearray. We need the type check because int(word, 16) would choke if word was already an `int.

if not isinstance(word, int):
    word = int(word, 16)

Check that the word isn't going break the formatting.

if word > 2**(8 * size) - 1:
    raise ValueError('word too great')

Finally, format the word, using keywords to decouple the string to the argument order.

return '{prefix}{word:0{padding}X}'.format(
        prefix=prefix,
        word=word,
        padding=2 * size)
\$\endgroup\$

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.