How to pad zeroes to a string?

前端 未结 17 1878
醉酒成梦
醉酒成梦 2020-11-21 22:59

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

相关标签:
17条回答
  • 2020-11-21 23:37

    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'
    
    0 讨论(0)
  • 2020-11-21 23:38

    Its ok too:

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

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

    0 讨论(0)
  • 2020-11-21 23:40

    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.

    0 讨论(0)
  • 2020-11-21 23:43

    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'
    
    0 讨论(0)
  • 2020-11-21 23:43

    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'
    
    
    0 讨论(0)
提交回复
热议问题