Làm thế nào để đệm số không vào một chuỗi?


Câu trả lời:


2394

Dây:

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

Và cho số:

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

Tài liệu định dạng chuỗi .


3
Mã định dạng không xác định 'd' cho đối tượng loại 'float'.
Cees Timmerman

7
Nhận xét python >= 2.6không chính xác. Cú pháp đó không hoạt động python >= 3. Bạn có thể thay đổi nó thành python < 3, nhưng tôi có thể đề nghị thay vì luôn luôn sử dụng dấu ngoặc đơn và bỏ hoàn toàn các nhận xét (khuyến khích sử dụng được khuyến nghị) không?
Jason R. Coombs

4
Lưu ý rằng bạn không cần đánh số các chuỗi định dạng của mình: '{:03d} {:03d}'.format(1, 2)ngầm định gán các giá trị theo thứ tự.
Rồng

1
@ JasonR.Coombs: Tôi giả sử bạn có nghĩa là printcâu lệnh, khi nào nó phải là một printhàm trên Python 3? Tôi đã chỉnh sửa trong parens; vì chỉ có một thứ đang được in, nên nó hoạt động giống hệt trên Py2 và Py3.
ShadowRanger


353

Chỉ cần sử dụng rjust phương pháp của đối tượng chuỗi.

Ví dụ này sẽ tạo một chuỗi dài 10 ký tự, đệm khi cần thiết.

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

123

Ngoài ra zfill, bạn có thể sử dụng định dạng chuỗi chung:

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

Tài liệu cho định dạng chuỗichuỗi f .


3
PEP 3101 không nêu rõ rằng% bị phản đối theo bất kỳ cách nào.
zwirbeltier

@zwirbeltier PEP 3101 giải thích cách sử dụng định dạng, ý tôi là vậy.
Konrad Rudolph

4
"EDIT" vẫn nêu "Cách thức định dạng này không được chấp nhận".
zwirbeltier

1
@zwirbeltier Có, và nó không được dùng nữa. Nhưng điều này không được nêu trực tiếp trong PEP. Tuy nhiên, tài liệu nói rằng nên sử dụng formatthay thế và mọi người thường hiểu đây là ý định phản đối.
Konrad Rudolph

1
@LarsH Cảm ơn bạn đã tìm thấy điều này. Vì vậy, chúng bị chậm tiến độ (Python 3.1 không có trong tương lai, nó ở quá khứ xa xôi). Do đó, tôi vẫn không nghĩ rằng câu trả lời là sai lệch, chỉ không được cập nhật nghiêm ngặt mỗi khi lịch trình phát triển Python thay đổi theo hướng mới, tùy ý. Dù sao, điều này đã cho tôi cơ hội để loại bỏ một số thứ không liên quan và lỗi thời khỏi câu trả lời của tôi.
Konrad Rudolph

63

Đối với Python 3.6+ sử dụng chuỗi f:

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

Đối với Python 2 đến Python 3.5:

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

56
>>> '99'.zfill(5)
'00099'
>>> '99'.rjust(5,'0')
'00099'

nếu bạn muốn ngược lại:

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

39

str(n).zfill(width)sẽ hoạt động với strings, ints, floats ... và tương thích với Python 2. x và 3. x :

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

23

Đối với những người đến đây để hiểu và không chỉ là một câu trả lời nhanh chóng. Tôi làm những điều này đặc biệt cho các chuỗi thời gian:

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

Ký hiệu "0" cần thay thế bằng ký tự đệm "2", mặc định là khoảng trống

Các ký hiệu ">" sắp xếp tất cả các ký tự 2 "0" ở bên trái chuỗi

":" ký hiệu format_spec


23

Cách pythonic nhất để đệm một chuỗi số có số 0 ở bên trái, nghĩa là, chuỗi số có độ dài cụ thể là gì?

str.zfill được dành riêng để làm điều này:

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

Lưu ý rằng nó được dành riêng để xử lý các chuỗi số theo yêu cầu và di chuyển một +hoặc -đến đầu chuỗi:

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

Đây là sự giúp đỡ về 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.

Hiệu suất

Đây cũng là cách hiệu quả nhất của các phương pháp thay thế:

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

Để so sánh tốt nhất táo với táo cho %phương pháp (lưu ý rằng nó thực sự chậm hơn), nếu không sẽ tính toán trước:

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

Thực hiện

Với một chút đào, tôi tìm thấy việc thực hiện zfillphương pháp trong 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;
}

Hãy đi qua mã C này.

Đầu tiên, nó phân tích cú pháp đối số theo vị trí, nghĩa là nó không cho phép đối số từ khóa:

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

Sau đó, nó kiểm tra xem nó có cùng độ dài hoặc dài hơn không, trong trường hợp đó nó trả về chuỗi.

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

zfillcuộc gọi pad(điều này padchức năng cũng được gọi bằng ljust, rjustcentercũng). Điều này về cơ bản sao chép nội dung thành một chuỗi mới và điền vào phần đệm.

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;
}

Sau khi gọi pad, zfilldi chuyển bất kỳ trước đó ban đầu +hoặc -đến đầu chuỗi.

Lưu ý rằng để chuỗi gốc thực sự là số là không bắt buộc:

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

về hiệu năng, có trường hợp chuỗi f nào tốt hơn bao gồm cả trường hợp sử dụng cho python2 vs python3 không? Ngoài ra, tôi nghĩ vì zfill không phổ biến, nó sẽ giúp câu trả lời của bạn có liên kết đến các tài liệu
elad silver

@eladsilver phụ thuộc vào ý định của bạn, ghi nhớ hành vi với +-, và tôi đã thêm một liên kết đến các tài liệu!
Aaron Hall

17
width = 10
x = 5
print "%0*d" % (width, x)
> 0000000005

Xem tài liệu in cho tất cả các chi tiết thú vị!

Cập nhật cho Python 3.x (7.5 năm sau)

Dòng cuối cùng bây giờ sẽ là:

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

Tức print()là bây giờ là một chức năng, không phải là một tuyên bố. Lưu ý rằng tôi vẫn thích printf()phong cách Trường học cũ bởi vì, IMNSHO, nó đọc tốt hơn và bởi vì, ừm, tôi đã sử dụng ký hiệu đó từ tháng 1 năm 1980. Một cái gì đó ... những con chó cũ .. một cái gì đó ... thủ thuật mới.


kể từ năm 1980 ... vậy bạn có phải là một lập trình viên 60 tuổi ... bạn có thể vui lòng giải thích thêm về cách "%0*d" % (width, x)giải thích của python không?
Lee

15

Khi sử dụng Python >= 3.6, cách sạch nhất là sử dụng chuỗi f với định dạng chuỗi :

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

Tôi muốn định dạng với một int, vì chỉ sau đó dấu hiệu được xử lý chính xác:

>>> f"{-1:08}"
'-0000001'

>>> f"{1:+08}"
'+0000001'

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

Cảm ơn ví dụ cú pháp mới. điền char 'x' sẽ là: v = "A18"; s = f '{v: x> 8}' + "|"; hoặc s = v.ljust (8, "x") + "|";
Charlie

@Charlie Đó là một câu hỏi cho tôi hay chỉ là một tuyên bố?
ruohola

chỉ là một tuyên bố. đã thử nghiệm một số cách sử dụng.
Charlie

4

Đối với mã zip được lưu dưới dạng số nguyên:

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

1
Bạn đã đúng, và dù sao thì tôi cũng thích đề xuất của bạn với zfill hơn

3

So sánh thời gian nhanh:

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]

Tôi đã thực hiện các thử nghiệm khác nhau về sự lặp lại khác nhau. Sự khác biệt không lớn, nhưng trong tất cả các thử nghiệm, zfillgiải pháp là nhanh nhất.


1

Một cách tiếp cận khác là sử dụng một sự hiểu biết danh sách với một điều kiện kiểm tra độ dài. Dưới đây là một minh chứng:

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

0

Nó cũng tốt

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

vì vậy đầu ra sẽ là: "02:07:03"


-2

Bạn cũng có thể lặp lại "0", thêm nó vào str(n)và lấy lát cắt có chiều rộng ngoài cùng bên phải. Nhanh và bẩn ít biểu hiện.

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

1
Điều này chỉ hoạt động cho số tích cực mặc dù. Nó sẽ phức tạp hơn một chút nếu bạn cũng muốn phủ định. Nhưng biểu hiện này là tốt cho công việc nhanh chóng và bẩn thỉu, nếu bạn không bận tâm đến điều đó.
J Lacar

Tôi hoàn toàn không biết tại sao điều này lại bị hạ thấp. Nếu nó gây ra nó không hoạt động trên các số âm đủ công bằng, nhưng lý do áp đảo người ta sẽ bỏ qua các số không là cho số id. Nếu bạn có số id âm, tôi nghĩ bạn có vấn đề lớn hơn ... bạn có đang mong đợi phần đệm của mình có dạng '00000-1234' không? hoặc '-000001234'? Thành thật đưa ra câu hỏi câu trả lời này hoạt động, nó đơn giản, sạch sẽ, nó có thể mở rộng. Nó có thể không phải là zfill nhưng nếu nó trả lời câu hỏi thì nó nên được nâng cấp.
TastySlowCooker
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.