Bagaimana cara memasukkan nol ke string?

1445

Apa yang dimaksud dengan cara Pythonic untuk mengisi string numerik dengan nol di sebelah kiri, sehingga string numerik memiliki panjang tertentu?

Nicolas Gervais
sumber

Jawaban:

2394

String:

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

Dan untuk angka:

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

Dokumentasi pemformatan string .

Harley Holcombe
sumber
3
Kode format tidak dikenal 'd' untuk objek tipe 'float'.
Cees Timmerman
7
Komentar python >= 2.6salah. Sintaks itu tidak berfungsi python >= 3. Anda dapat mengubahnya python < 3, tetapi bolehkah saya menyarankan untuk selalu menggunakan tanda kurung dan menghilangkan komentar sama sekali (mendorong penggunaan yang disarankan)?
Jason R. Coombs
4
Perhatikan bahwa Anda tidak perlu memberi nomor string format Anda: '{:03d} {:03d}'.format(1, 2)secara implisit memberikan nilai-nilai secara berurutan.
Naga
1
@ JasonR.Coombs: Saya berasumsi Anda maksud printpernyataan itu, padahal seharusnya printfungsi pada Python 3? Saya mengedit di parens; karena hanya satu hal yang sedang dicetak, ia bekerja secara identik sekarang di Py2 dan Py3.
ShadowRanger
353

Cukup gunakan metode rjust dari objek string.

Contoh ini akan membuat string sepanjang 10 karakter, padding sesuai kebutuhan.

>>> t = 'test'
>>> t.rjust(10, '0')
>>> '000000test'
Paul D. Eden
sumber
123

Selain itu zfill, Anda dapat menggunakan pemformatan string umum:

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

Dokumentasi untuk pemformatan string dan f-string .

Konrad Rudolph
sumber
3
PEP 3101 tidak menyatakan bahwa% tidak digunakan lagi.
zwirbeltier
@zwirbeltier PEP 3101 menjelaskan cara menggunakan format, itulah yang saya maksud.
Konrad Rudolph
4
"EDIT" masih menyatakan "... metode pemformatan ini tidak digunakan lagi ...".
zwirbeltier
1
@ zwirbeltier Ya, dan sudah usang. Tetapi ini tidak secara langsung dinyatakan dalam PEP. Dokumentasi, bagaimanapun, mengatakan untuk digunakan formatsebagai gantinya, dan orang-orang umumnya menafsirkan ini sebagai niat untuk mundur.
Konrad Rudolph
1
@ LarsH Terima kasih telah menemukan ini. Jadi mereka sangat terlambat dari jadwal (Python 3.1 tidak di masa depan, itu di masa lalu yang jauh). Mengingat itu, saya masih tidak berpikir jawabannya menyesatkan, hanya saja tidak diperbarui secara ketat setiap kali jadwal pengembangan Python berubah ke arah yang baru dan sewenang-wenang. Bagaimanapun, ini memberi saya kesempatan untuk menghapus beberapa hal yang tidak relevan dan ketinggalan zaman dari jawaban saya.
Konrad Rudolph
63

Untuk Python 3.6+ menggunakan f-string:

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

Untuk Python 2 hingga Python 3.5:

>>> "{:0>2}".format("1")  # Works for both numbers and strings.
'01'
>>> "{:02}".format(1)  # Works only for numbers.
'01'
Cees Timmerman
sumber
56
>>> '99'.zfill(5)
'00099'
>>> '99'.rjust(5,'0')
'00099'

jika Anda menginginkan yang sebaliknya:

>>> '99'.ljust(5,'0')
'99000'
Victor Barrantes
sumber
39

str(n).zfill(width)akan bekerja dengan strings, ints, floats ... dan Python 2. x dan 3. x kompatibel:

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

Bagi mereka yang datang ke sini untuk mengerti dan bukan hanya jawaban cepat. Saya melakukan ini terutama untuk string waktu:

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" melambangkan apa yang harus diganti dengan karakter padding "2", standarnya adalah ruang kosong

">" simbol mengatur semua karakter 2 "0" di sebelah kiri string

":" melambangkan format_spec

elad perak
sumber
23

Apa cara paling pythonic untuk mengisi string numerik dengan nol ke kiri, yaitu, sehingga string numerik memiliki panjang tertentu?

str.zfill secara khusus dimaksudkan untuk melakukan ini:

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

Perhatikan bahwa ini secara khusus dimaksudkan untuk menangani string numerik seperti yang diminta, dan memindahkan a +atau -ke awal string:

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

Inilah bantuan untuk 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.

Performa

Ini juga yang paling banyak melakukan metode alternatif:

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

Untuk membandingkan apel dengan apel untuk %metode ini (perhatikan bahwa sebenarnya lebih lambat), yang sebaliknya akan melakukan pra-perhitungan:

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

Penerapan

Dengan sedikit penggalian, saya menemukan implementasi zfillmetode di 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;
}

Mari kita telusuri kode C ini.

Pertama mem-parsing argumen secara posisi, artinya tidak mengizinkan argumen kata kunci:

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

Itu kemudian memeriksa apakah itu panjang yang sama atau lebih lama, dalam hal ini mengembalikan string.

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

zfillpanggilan pad(ini padfungsi disebut juga dengan ljust, rjust, dan centerjuga). Ini pada dasarnya menyalin konten ke string baru dan mengisi 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;
}

Setelah memanggil pad, zfillpindahkan semua yang awalnya sebelum +atau -ke awal string.

Perhatikan bahwa untuk string asli menjadi numerik tidak diperlukan:

>>> '+foo'.zfill(10)
'+000000foo'
>>> '-foo'.zfill(10)
'-000000foo'
Aaron Hall
sumber
untuk kinerja, apakah ada kasus di mana f string lebih baik termasuk kasus penggunaan untuk python2 vs python3? juga, saya pikir karena zfill tidak umum itu akan membantu jawaban Anda untuk memiliki tautan ke dokumen
elad silver
@eladsilver tergantung pada niat Anda, mengingat perilaku dengan +dan -, dan saya menambahkan tautan ke dokumen!
Aaron Hall
17
width = 10
x = 5
print "%0*d" % (width, x)
> 0000000005

Lihat dokumentasi cetak untuk semua detail menarik!

Pembaruan untuk Python 3.x (7,5 tahun kemudian)

Baris terakhir itu sekarang adalah:

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

Yaitu print()sekarang fungsi, bukan pernyataan. Perhatikan bahwa saya masih lebih suka printf()gaya Sekolah Lama karena, IMNSHO, bunyinya lebih baik, dan karena, um, saya telah menggunakan notasi itu sejak Januari 1980. Sesuatu ... anjing-anjing tua ... sesuatu yang ... trik-trik baru.

Peter Rowell
sumber
sejak 1980 ... jadi Anda seorang programmer berusia 60 tahun ... bisakah Anda memberikan penjelasan lebih lanjut tentang bagaimana "%0*d" % (width, x)ditafsirkan oleh python?
Lee
15

Saat menggunakan Python >= 3.6, cara terbersih adalah menggunakan f-string dengan pemformatan string :

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

Saya lebih suka memformat dengan int, karena hanya saat itu tanda ditangani dengan benar:

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

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

>>> f"{'-1':0>8}"
'000000-1'
ruohola
sumber
Terima kasih untuk contoh sintaks baru. isi char 'x' adalah: v = "A18"; s = f '{v: x> 8}' + "|"; atau s = v.ljust (8, "x") + "|";
Charlie 木匠
@Charlie 木匠 Apakah itu pertanyaan bagi saya atau hanya sebuah pernyataan?
ruohola
hanya sebuah pernyataan. menguji beberapa penggunaan lagi.
Charlie 木匠
4

Untuk kode pos disimpan sebagai bilangan bulat:

>>> a = 6340
>>> b = 90210
>>> print '%05d' % a
06340
>>> print '%05d' % b
90210
Acumenus
sumber
1
Anda benar, dan saya suka saran Anda dengan zfill lebih baik
3

Perbandingan waktu cepat:

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]

Saya telah membuat berbagai tes pengulangan yang berbeda. Perbedaannya tidak besar, tetapi dalam semua tes, zfillsolusinya paling cepat.

Simon Steinberger
sumber
1

Pendekatan lain adalah dengan menggunakan pemahaman daftar dengan kondisi memeriksa panjang. Di bawah ini adalah demonstrasi:

# 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']
kmario23
sumber
0

Ok juga:

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

jadi output akan menjadi: "02:07:03"

zzfima
sumber
-2

Anda juga bisa mengulangi "0", tambahkan dulu str(n)dan dapatkan irisan lebar paling kanan. Ekspresi kecil yang cepat dan kotor.

def pad_left(n, width, pad="0"):
    return ((pad * width) + str(n))[-width:]
J Lacar
sumber
1
Ini hanya bekerja untuk angka positif. Menjadi sedikit lebih rumit jika Anda ingin negatif juga. Tetapi ungkapan ini bagus untuk pekerjaan yang cepat dan kotor, jika Anda tidak keberatan dengan hal semacam itu.
J Lacar
Saya sama sekali tidak tahu mengapa ini diturunkan. Jika itu disebabkan itu tidak bekerja pada angka negatif cukup adil, tetapi alasan luar biasa seseorang akan meninggalkan pad dengan nol adalah untuk nomor id. Jika Anda memiliki nomor id negatif, saya pikir Anda memiliki masalah lebih besar ... apakah Anda mengharapkan buku catatan Anda berupa '00000-1234'? atau '-000001234'? Jujur diberi pertanyaan jawaban ini berfungsi, sederhana, bersih, dapat diperpanjang. Ini mungkin bukan zfill tetapi jika menjawab pertanyaan itu harus dibatalkan.
TastySlowCooker