2250

How do I pad a numeric string with zeroes to the left, so that the string has a specific length?

0

19 Answers 19

3499

To pad strings:

>>> n = '4' >>> print(n.zfill(3)) 004 

To pad numbers:

>>> n = 4 >>> print(f'{n:03}') # Preferred method, python >= 3.6 004 >>> print('%03d' % n) 004 >>> print(format(n, '03')) # python >= 2.6 004 >>> print('{0:03d}'.format(n)) # python >= 2.6 + python 3 004 >>> print('{foo:03d}'.format(foo=n)) # python >= 2.6 + python 3 004 >>> print('{:03d}'.format(n)) # python >= 2.7 + python3 004 

String formatting documentation.

Sign up to request clarification or add additional context in comments.

8 Comments

Comments python >= 2.6 are incorrect. That syntax doesn't work on python >= 3. You could change it to python < 3, but may I suggest instead always using parenthesis and omitting the comments altogether (encouraging recommended usage)?
Note that you don't need to number your format strings: '{:03d} {:03d}'.format(1, 2) implicitly assigns the values in order.
Can any of these approaches be adapted to work with a variable number of zeroes?
How could you have not used the number 7 for your example?!? 😲
@Motti It's probably just a random number xkcd.com/221
|
505

Just use the rjust method of the string object.

This example creates a 10-character length string, padding as necessary:

>>> s = 'test' >>> s.rjust(10, '0') >>> '000000test' 

3 Comments

In my opinion, it should be 't = t.rjust(10, '0'), otherwise the value of t remains unchanged (at least for me)
@StanislavKoncebovski strings are immutable in Python. The value of a string will always remain unchanged no matter what you do to it and you always have to reassign if you want to update the variable to reference the new string. This has nothing to do with rjust.
@Paul D.Eden You may be right theoretically, but I checked it again, and yes, if you do not assign like t = t.rjust(10, '0') you will not obtain '000000test' in t. My assertion is based on a test. I am using Python 3.7.
262

For Python 3.6+ using f-strings:

>>> i = 1 >>> f"{i:0>2}" # Works for both numbers and strings. '01' >>> f"{i:02}" # Works only for numbers. '01' 

For Python 2.6 to Python 3.5:

>>> "{:0>2}".format("1") # Works for both numbers and strings. '01' >>> "{:02}".format(1) # Works only for numbers. '01' 

Those standard format specifiers are [[fill]align][minimumwidth] and [0][minimumwidth].

3 Comments

Thx. Where can we find the official doc of things like f"{i:0>2}" ? The only official doc about f-string I can find is peps.python.org/pep-0498/#format-specifiers, but I get no result when I type ctrl-f then type in pad or fill.
@GoodPen Unfortunately, Python docs never caught up to PHP docs ease, but i found it at peps.python.org/pep-3101/#standard-format-specifiers
Now I find it by pydoc FORMATING
222

Besides zfill, you can use general string formatting:

print(f'{number:05d}') # (since Python 3.6), or print('{:05d}'.format(number)) # or print('{0:05d}'.format(number)) # or (explicit 0th positional arg. selection) print('{n:05d}'.format(n=number)) # or (explicit `n` keyword arg. selection) print(format(number, '05d')) 

Documentation for string formatting and f-strings.

2 Comments

@Konrad: "The documentation, however, says to use format instead." I know I'm late to the party, but I'd like to see what you mean by this. The documentation I see (docs.python.org/3/library/stdtypes.html#old-string-formatting) says using format or other alternatives "may help avoid [aforementioned] errors" associated with % interpolation. That's not very robust "deprecation."
@LarsH Well it’s notable that the link in my answer originally pointed to % formatting. It now points to str.format formatting. I did not change the link! Rather, the Python documentation website behind that link was rewritten. Apart from that, the documentation used to have stronger wording, and literally states that str.format “should be preferred to the % formatting”, just as I wrote in the comment you quoted.
78
>>> '99'.zfill(5) '00099' >>> '99'.rjust(5,'0') '00099' 

if you want the opposite:

>>> '99'.ljust(5,'0') '99000' 

1 Comment

Note that zfill and ljust behave differently on negative numbers.
45

str(n).zfill(width) will work with strings, ints, floats... and is Python 2.x and 3.x compatible:

>>> n = 3 >>> str(n).zfill(5) '00003' >>> n = '3' >>> str(n).zfill(5) '00003' >>> n = '3.0' >>> str(n).zfill(5) '003.0' 

Comments

37

What is the most pythonic way to pad a numeric string with zeroes to the left, i.e., so the numeric string has a specific length?

str.zfill is specifically intended to do this:

>>> '1'.zfill(4) '0001' 

Note that it is specifically intended to handle numeric strings as requested, and moves a + or - to the beginning of the string:

>>> '+1'.zfill(4) '+001' >>> '-1'.zfill(4) '-001' 

Here's the help on str.zfill:

>>> help(str.zfill) Help on method_descriptor: zfill(...) S.zfill(width) -> str Pad a numeric string S with zeros on the left, to fill a field of the specified width. The string S is never truncated. 

Performance

This is also the most performant of alternative methods:

>>> min(timeit.repeat(lambda: '1'.zfill(4))) 0.18824880896136165 >>> min(timeit.repeat(lambda: '1'.rjust(4, '0'))) 0.2104538488201797 >>> min(timeit.repeat(lambda: f'{1:04}')) 0.32585487607866526 >>> min(timeit.repeat(lambda: '{:04}'.format(1))) 0.34988890308886766 

To best compare apples to apples for the % method (note it is actually slower), which will otherwise pre-calculate:

>>> min(timeit.repeat(lambda: '1'.zfill(0 or 4))) 0.19728074967861176 >>> min(timeit.repeat(lambda: '%04d' % (0 or 1))) 0.2347015216946602 

Implementation

With a little digging, I found the implementation of the zfill method in Objects/stringlib/transmogrify.h:

static PyObject * stringlib_zfill(PyObject *self, PyObject *args) { Py_ssize_t fill; PyObject *s; char *p; Py_ssize_t width; if (!PyArg_ParseTuple(args, "n:zfill", &width)) return NULL; if (STRINGLIB_LEN(self) >= width) { return return_self(self); } fill = width - STRINGLIB_LEN(self); s = pad(self, fill, 0, '0'); if (s == NULL) return NULL; p = STRINGLIB_STR(s); if (p[fill] == '+' || p[fill] == '-') { /* move sign to beginning of string */ p[0] = p[fill]; p[fill] = '0'; } return s; } 

Let's walk through this C code.

It first parses the argument positionally, meaning it doesn't allow keyword arguments:

>>> '1'.zfill(width=4) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: zfill() takes no keyword arguments 

It then checks if it's the same length or longer, in which case it returns the string.

>>> '1'.zfill(0) '1' 

zfill calls pad (this pad function is also called by ljust, rjust, and center as well). This basically copies the contents into a new string and fills in the padding.

static inline PyObject * pad(PyObject *self, Py_ssize_t left, Py_ssize_t right, char fill) { PyObject *u; if (left < 0) left = 0; if (right < 0) right = 0; if (left == 0 && right == 0) { return return_self(self); } u = STRINGLIB_NEW(NULL, left + STRINGLIB_LEN(self) + right); if (u) { if (left) memset(STRINGLIB_STR(u), fill, left); memcpy(STRINGLIB_STR(u) + left, STRINGLIB_STR(self), STRINGLIB_LEN(self)); if (right) memset(STRINGLIB_STR(u) + left + STRINGLIB_LEN(self), fill, right); } return u; } 

After calling pad, zfill moves any originally preceding + or - to the beginning of the string.

Note that for the original string to actually be numeric is not required:

>>> '+foo'.zfill(10) '+000000foo' >>> '-foo'.zfill(10) '-000000foo' 

2 Comments

for the performance, are there cases where f strings are better including use cases for python2 vs python3? also, I think as zfill is not common it would help your answer to have a link to the docs
@eladsilver depends on your intent, keeping in mind the behavior with + and -, and I added a link to the docs!
32

For the ones who came here to understand and not just a quick answer. I do these especially for time strings:

hour = 4 minute = 3 "{:0>2}:{:0>2}".format(hour,minute) # prints 04:03 "{:0>3}:{:0>5}".format(hour,minute) # prints '004:00003' "{:0<3}:{:0<5}".format(hour,minute) # prints '400:30000' "{:$<3}:{:#<5}".format(hour,minute) # prints '4$$:3####' 

"0" symbols what to replace with the "2" padding characters, the default is an empty space

">" symbols allign all the 2 "0" character to the left of the string

":" symbols the format_spec

Comments

30

When using Python >= 3.6, the cleanest way is to use f-strings with string formatting:

>>> s = f"{1:08}" # inline with int >>> s '00000001' 
>>> s = f"{'1':0>8}" # inline with str >>> s '00000001' 
>>> n = 1 >>> s = f"{n:08}" # int variable >>> s '00000001' 
>>> c = "1" >>> s = f"{c:0>8}" # str variable >>> s '00000001' 

I would prefer formatting with an int, since only then the sign is handled correctly:

>>> f"{-1:08}" '-0000001' >>> f"{1:+08}" '+0000001' >>> f"{'-1':0>8}" '000000-1' 

Comments

23

For numbers:

i = 12 print(f"{i:05d}") 

Output

00012 

Comments

21

I am adding how to use a int from a length of a string within an f-string because it didn't appear to be covered:

>>> pad_number = len("this_string") 11 >>> s = f"{1:0{pad_number}}" } >>> s '00000000001' 

1 Comment

it's covered in this answer: stackoverflow.com/a/57360675/860196
19
width = 10 x = 5 print "%0*d" % (width, x) > 0000000005 

See the print documentation for all the exciting details!

Update for Python 3.x (7.5 years later)

That last line should now be:

print("%0*d" % (width, x)) 

I.e. print() is now a function, not a statement. Note that I still prefer the Old School printf() style because, IMNSHO, it reads better, and because, um, I've been using that notation since January, 1980. Something ... old dogs .. something something ... new tricks.

Comments

7

For zip codes saved as integers:

>>> a = 6340 >>> b = 90210 >>> print '%05d' % a 06340 >>> print '%05d' % b 90210 

1 Comment

You are correct, and I like your suggestion with zfill better anyhow
5

Quick timing comparison:

setup = ''' from random import randint def test_1(): num = randint(0,1000000) return str(num).zfill(7) def test_2(): num = randint(0,1000000) return format(num, '07') def test_3(): num = randint(0,1000000) return '{0:07d}'.format(num) def test_4(): num = randint(0,1000000) return format(num, '07d') def test_5(): num = randint(0,1000000) return '{:07d}'.format(num) def test_6(): num = randint(0,1000000) return '{x:07d}'.format(x=num) def test_7(): num = randint(0,1000000) return str(num).rjust(7, '0') ''' import timeit print timeit.Timer("test_1()", setup=setup).repeat(3, 900000) print timeit.Timer("test_2()", setup=setup).repeat(3, 900000) print timeit.Timer("test_3()", setup=setup).repeat(3, 900000) print timeit.Timer("test_4()", setup=setup).repeat(3, 900000) print timeit.Timer("test_5()", setup=setup).repeat(3, 900000) print timeit.Timer("test_6()", setup=setup).repeat(3, 900000) print timeit.Timer("test_7()", setup=setup).repeat(3, 900000) > [2.281613943830961, 2.2719342631547077, 2.261691106209631] > [2.311480238815406, 2.318420542148333, 2.3552384305184493] > [2.3824197456864304, 2.3457239951596485, 2.3353268829498646] > [2.312442972404032, 2.318053102249902, 2.3054072168069872] > [2.3482314132374853, 2.3403386400002475, 2.330108825844775] > [2.424549090688892, 2.4346475296851438, 2.429691196530058] > [2.3259756401716487, 2.333549212826732, 2.32049893822186] 

I've made different tests of different repetitions. The differences are not huge, but in all tests, the zfill solution was fastest.

Comments

5

If you're looking to pad an integer, and limit the significant figures at the same time (with f strings):

a = 4.432 >> 4.432 a = f'{a:04.1f}' >> '04.4' 

f'{a:04.1f}' this translates to 1 decimal/(float) point, left pad the digit until 4 characters total.

Comments

2

Its ok too:

 h = 2 m = 7 s = 3 print("%02d:%02d:%02d" % (h, m, s)) 

so output will be: "02:07:03"

Comments

1

You could also repeat "0", prepend it to str(n) and get the rightmost width slice. Quick and dirty little expression.

def pad_left(n, width, pad="0"): return ((pad * width) + str(n))[-width:] 

2 Comments

This only works for positive numbers though. It gets a little more complicated if you want negatives too. But this expression is good for quick and dirty work, if you don't mind that kind of thing.
I have absolutely no idea why this is downvoted. If it's cause it doesn't work on negative numbers fair enough, but the overwhelming reason one would left pad with zeros is for id numbers. If you have negative id numbers I think you have bigger problems... are you expecting your pad to be of the form '00000-1234'? or '-000001234'? Honestly given the question this answer works, it's simple, it's clean, it's extensible. It may not be zfill but if it answers the question it should be upvoted.
1

Another approach would be to use a list comprehension with a condition checking for lengths. Below is a demonstration:

# input list of strings that we want to prepend zeros In [71]: list_of_str = ["101010", "10101010", "11110", "0000"] # prepend zeros to make each string to length 8, if length of string is less than 8 In [83]: ["0"*(8-len(s)) + s if len(s) < desired_len else s for s in list_of_str] Out[83]: ['00101010', '10101010', '00011110', '00000000'] 

Comments

0

I made a function :

def PadNumber(number, n_pad, add_prefix=None): number_str = str(number) paded_number = number_str.zfill(n_pad) if add_prefix: paded_number = add_prefix+paded_number print(paded_number) PadNumber(99, 4) PadNumber(1011, 8, "b'") PadNumber('7BEF', 6, "#") 

The output :

0099 b'00001011 #007BEF 

Comments