ChatGPT解决这个技术问题 Extra ChatGPT

How do I pad a string with zeroes?

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


M
Mateen Ulhaq

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.


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.
@JasonR.Coombs: I assume you meant the print statement, when it should be a print function on Python 3? I edited in the parens; since only one thing is being printed, it works identically now on Py2 and Py3.
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?!? 😲
M
Mateen Ulhaq

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'

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.
never heard of that method before - good at hiding but now it has been "out'ed" !
@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.
D
Daniel Morell

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.


@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.
A
Asclepius

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 to Python 3.5:

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

V
Victor Barrantes
>>> '99'.zfill(5)
'00099'
>>> '99'.rjust(5,'0')
'00099'

if you want the opposite:

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

j
johnsyweb

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'

R
Russia Must Remove Putin

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'

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!
e
elad silver

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


r
ruohola

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'

u
user1315621

For numbers:

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

Output

00012

I'd rather hope you'll get '00002'. Also an example with i>10 might have been a good idea. Nonetheless, this helped me.
P
Peter Rowell
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.


since 1980... so are you a 60 years old programmer... could you please give more explanation on how "%0*d" % (width, x) is interpreted by python?
N
NBStephens

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'


it's covered in this answer: stackoverflow.com/a/57360675/860196
A
Asclepius

For zip codes saved as integers:

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

You are correct, and I like your suggestion with zfill better anyhow
S
Simon Steinberger

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.


z
zzfima

Its ok too:

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

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


k
kmario23

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']

J
Julien Faujanet

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

L
Lafftar

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.


D
Danra

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:]

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.