简体   繁体   中英

Can f-strings auto-pad to [the next] even number of digits on output?

Based on this answer (among others) it seems like f-strings is [one of] the preferred ways to convert to hexadecimal representation.

While one can specify an explicit target length, up to which to pad with leading zeroes, given a goal of an output with an even number of digits , and inputs with an arbitrary # of bits, I can imagine:

  • pre-processing to determine the number of bits of the input, to feed an input-specific value in to the fstring, or
  • post-processing a-la out = "0"+f"{val:x}" if len(f"{val:x}") % 2 else f"{val:02x}" (or even using .zfill() )

The latter seems like it might be more efficient than the former - is there a built-in way to do this with fstrings, or a better alternative?

Examples of input + expected output:

[0x]1 -> [0x]01
[0x]22 -> [0x]22
[0x]333 -> [0x]0333
[0x]4444 -> [0x]4444

and so on.

You can use a variable in the pad-length part of the f-string. For example:

n = 4
val = 257
print(f"{val:0{n}x}") # 0101

Now, to figure out how many hex characters are in an integer, you just need to find how many bits are in the integer:

hex_count, rem = divmod(max(1, val.bit_length()), 4)
hex_count += (rem > 0)

( max(1, val.bit_length()) handles the case where val == 0 , which has a bit length of 0)

So let's get the next even number after hex_count :

pad_length = hex_count + (hex_count % 2)
print(f"{val:0{pad_length}x}") # 0101

I'm not sure if this is any better than simply converting it to a hex string and then figuring out how much padding is needed, but I can't think of a readable way to do this all in an f-string. An unreadable way would be by combining all of the above into a single line, but IMO readable code is better than unreadable one-liners. I don't think there's a way to specify what you want as a simple f-string.

Note that negative numbers are formatted to an even number of digits, plus the - sign.

I don't think there's anything built in to f-string formatting that will do this. You probably have to figure out what the "natural" width would be then round that up to the next even number.

Something like this:

def hf(n):
    width = len(hex(n)) - 2 # account for leading 0x
    width += width % 2 # round up
    return f'{n:0{width}x}'

print(hf(1))
print(hf(15))
print(hf(16))
print(hf(255))
print(hf(256))

Output:

01
0f
10
ff
0100

Here's a postprocessing alternative that uses assignment expressions (Python 3.8+):

print((len(hx:=f"{val:x}") % 2) * '0' + hx)

If you still want a one-liner without assignment expressions you have to evaluate your f-string twice:

print((len(f"{val:x}") % 2) * '0' + f"{val:x}")

As a two-liner

hx = f"{val:x}"
print((len(hx) % 2) * '0' + hx)

And one more version:

print(f"{'0'[:len(hex(val))%2]}{val:x}")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM