moooeeeep
moooeeeep

Reputation: 32502

Correct way to format integers with fixed length and space padding

I have integer numbers in the range of 0 up until (including) 100. I want to convert them to strings of a fixed length of 3 with space padding and alignment to the right.

I've tried to use the following format string, but it adds another space for three digit numbers, which makes them length 4 instead of 3.

fmt = lambda x: "{: 3d}".format(x)
[fmt(99), fmt(100)] # produces [' 99', ' 100'] instead of [' 99', '100']

Interestingly, it works as expected when zero-padding is used:

fmt = lambda x: "{:03d}".format(x) 
[fmt(99), fmt(100)] # produces ['099', '100'] as expected

Why is this? How can I fix this?

Do I really need to convert to string first?

fmt = lambda x: "{:>3s}".format(str(x))
[fmt(99), fmt(100)] # produces [' 99', '100'] as expected

Upvotes: 18

Views: 21293

Answers (3)

Eugene Yarmash
Eugene Yarmash

Reputation: 149756

By default, numbers are aligned to the right and padded with spaces when formatted, so you should just specify the width:

>>> '{:3d}'.format(99)
' 99'
>>> '{:3d}'.format(100)
'100'

Alternatively, you can specify both the fill character and alignment:

>>> '{: >3d}'.format(99)
' 99'
>>> '{: >3d}'.format(100)
'100'

A single space before width, however, is treated as the sign option. Quoting the documentation:

The sign option is only valid for number types, and can be one of the following:

'+' indicates that a sign should be used for both positive as well as negative numbers.
'-' indicates that a sign should be used only for negative numbers (this is the default behavior).
' ' indicates that a leading space should be used on positive numbers, and a minus sign on negative numbers.

That's why "{: 3d}" formats with a leading space in your examples.

Upvotes: 37

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 250931

The reason it is not working for numbers is because space is being considered a sign character.

Indicates that a leading space should be used on positive numbers, and a minus sign on negative numbers.

Hence there's always a leading space in your numbers.

On the other hand fill value expects a align value to be specified first:

If a valid align value is specified, it can be preceded by a fill character that can be any character and defaults to a space if omitted.

But it works in case of '0' with no align value because it is a special case:

When no explicit alignment is given, preceding the width field by a zero ('0') character enables sign-aware zero-padding for numeric types. This is equivalent to a fill character of '0' with an alignment type of '='.

But won't work for other fill values:

>>> '{:*5d}'.format(100)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-10-559a168f3704> in <module>()
----> 1 '{:*5d}'.format(100)

ValueError: Invalid format specifier
>>> '{:*=5d}'.format(100)
'**100'

Hence in your case, as the default fill value is space you can simply drop it to prevent specifying alignment:

>>> '{:3d}'.format(99)
' 99'    
>>> '{:3d}'.format(100)
'100'

# With alignment and fill character

>>> '{: =3d}'.format(99)
' 99'    
>>> '{: =3d}'.format(100)
'100'

Upvotes: 3

You need not convert to string, the issue with above code is you used "{: 3d}".format(x) instead of "{:3d}".format(x)

   fmt = lambda x: "{:3d}".format(x)
   [fmt(99), fmt(100)] #produces [' 99', '100']

Upvotes: 1

Related Questions