Come riempire gli zeri di una stringa?


1445

Qual è un modo Pythonic per riempire una stringa numerica con zero a sinistra, cioè quindi la stringa numerica ha una lunghezza specifica?

Risposte:


2394

Stringhe:

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

E per i numeri:

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

Documentazione sulla formattazione delle stringhe .


3
Codice formato sconosciuto 'd' per oggetto di tipo 'float'.
Cees Timmerman,

7
I commenti python >= 2.6non sono corretti Questa sintassi non funziona python >= 3. Potresti cambiarlo in python < 3, ma posso suggerire invece di utilizzare sempre la parentesi e omettere del tutto i commenti (incoraggiando l'uso raccomandato)?
Jason R. Coombs,

4
Nota che non è necessario numerare le stringhe di formato: '{:03d} {:03d}'.format(1, 2)assegna implicitamente i valori in ordine.
Dragon,

1
@ JasonR.Coombs: suppongo che intendessi la printfrase, quando dovrebbe essere una printfunzione su Python 3? Ho curato tra parentesi; poiché viene stampata una sola cosa, ora funziona in modo identico su Py2 e Py3.
ShadowRanger


353

Usa semplicemente il metodo rjust dell'oggetto stringa.

Questo esempio renderà una stringa di 10 caratteri, riempiendo se necessario.

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

123

Inoltre zfill, puoi utilizzare la formattazione generale delle stringhe:

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

Documentazione per la formattazione di stringhe e f-stringhe .


3
PEP 3101 non afferma che% è deprecato in alcun modo.
zwirbeltier,

@zwirbeltier PEP 3101 spiega come utilizzare il formato, è ciò che intendevo.
Konrad Rudolph,

4
"EDIT" indica ancora "... questo metodo di formattazione è obsoleto ...".
zwirbeltier,

1
@zwirbeltier Sì, ed è obsoleto. Ma questo non è dichiarato direttamente nel PEP. La documentazione, tuttavia, dice di usare formatinvece, e le persone generalmente interpretano questo come intenzione di deprecare.
Konrad Rudolph,

1
@LarsH Grazie per aver trovato questo. Quindi sono in grave ritardo (Python 3.1 non è in futuro, è in un lontano passato). Detto questo, non credo ancora che la risposta sia stata fuorviante, ma non costantemente aggiornata ogni volta che il programma di sviluppo di Python cambiava in una nuova direzione arbitraria. Comunque, questo mi ha dato l'opportunità di rimuovere dalla mia risposta alcune cose irrilevanti e obsolete.
Konrad Rudolph,

63

Per Python 3.6+ usando le stringhe f:

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

Per Python 2 a 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'

se vuoi il contrario:

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

39

str(n).zfill(width)funzionerà con strings, ints, floats ... ed è compatibile con Python 2. xe 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

Per quelli che sono venuti qui per capire e non solo una risposta rapida. Faccio questi in particolare per le stringhe di tempo:

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" simboli cosa sostituire con i caratteri di riempimento "2", il valore predefinito è uno spazio vuoto

I simboli ">" assegnano tutti i 2 caratteri "0" a sinistra della stringa

":" simboleggia il format_spec


23

Qual è il modo più pitonico per riempire una stringa numerica con zero a sinistra, cioè, quindi la stringa numerica ha una lunghezza specifica?

str.zfill è specificamente destinato a fare questo:

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

Si noti che è specificamente progettato per gestire le stringhe numeriche come richiesto e sposta una +o -all'inizio della stringa:

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

Ecco l'aiuto su 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.

Prestazione

Questo è anche il metodo più performante di metodi alternativi:

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

Per confrontare meglio le mele con le mele per il %metodo (nota che in realtà è più lento), che altrimenti pre-calcolerà:

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

Implementazione

Con un po 'di scavo, ho trovato l'implementazione del zfillmetodo 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;
}

Esaminiamo questo codice C.

Prima analizza l'argomento in modo posizionale, nel senso che non consente argomenti di parole chiave:

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

Quindi controlla se è della stessa lunghezza o più lunga, nel qual caso restituisce la stringa.

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

zfillchiamate pad(questa padfunzione è chiamato anche ljust, rjuste centerpure). Questo sostanzialmente copia il contenuto in una nuova stringa e riempie il riempimento.

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

Dopo aver chiamato pad, zfillsposta qualsiasi precedente +o -all'inizio della stringa.

Non è necessario che la stringa originale sia effettivamente numerica:

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

per le prestazioni, ci sono casi in cui le stringhe f sono migliori, inclusi i casi d'uso per python2 vs python3? inoltre, penso che zfill non sia comune, aiuterebbe la tua risposta ad avere un collegamento ai documenti
elad silver

@eladsilver dipende dalle tue intenzioni, tenendo presente il comportamento con +e -, e ho aggiunto un link ai documenti!
Aaron Hall

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

Consulta la documentazione di stampa per tutti i dettagli entusiasmanti!

Aggiornamento per Python 3.x (7.5 anni dopo)

L'ultima riga dovrebbe ora essere:

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

Cioè print()ora è una funzione, non un'affermazione. Nota che preferisco ancora lo printf()stile Old School perché, IMNSHO, legge meglio, e perché, um, uso questa notazione dal gennaio 1980. Qualcosa ... vecchi cani ... qualcosa qualcosa ... nuovi trucchi.


dal 1980 ... quindi sei un programmatore di 60 anni ... potresti per favore dare maggiori spiegazioni su come "%0*d" % (width, x)viene interpretato da Python?
Lee,

15

Quando si utilizza Python >= 3.6, il modo più pulito è utilizzare le stringhe f con formattazione delle stringhe :

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

Preferirei formattare con un int, poiché solo allora il segno viene gestito correttamente:

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

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

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

Grazie per il nuovo esempio di sintassi. riempire il carattere 'x' sarà: v = "A18"; s = f '{v: x> 8}' + "|"; oppure s = v.ljust (8, "x") + "|";
Charlie 木匠 il

@Charlie 木匠 Era una domanda per me o solo una dichiarazione?
ruohola,

solo una dichiarazione. testato qualche altro utilizzo.
Charlie 木匠

4

Per i codici postali salvati come numeri interi:

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

1
Hai ragione, e mi piace comunque il tuo suggerimento con zfill meglio

3

Confronto dei tempi rapidi:

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]

Ho fatto diversi test di diverse ripetizioni. Le differenze non sono enormi, ma in tutti i test la zfillsoluzione è stata la più veloce.


1

Un altro approccio sarebbe quello di utilizzare una comprensione dell'elenco con una condizione che controlla le lunghezze. Di seguito è una dimostrazione:

# 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

Anche ok:

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

quindi l'output sarà: "02:07:03"


-2

Puoi anche ripetere "0", anteporre a str(n)e ottenere la sezione di larghezza più a destra. Espressione piccola e veloce.

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

1
Questo funziona solo con numeri positivi. Diventa un po 'più complicato se vuoi anche i negativi. Ma questa espressione è buona per il lavoro veloce e sporco, se non ti dispiace quel genere di cose.
J Lacar,

Non ho assolutamente idea del perché questo sia sottoposto a downgrade. Se la causa è che non funziona su numeri negativi abbastanza equi, ma la ragione schiacciante che uno avrebbe lasciato pad con zeri è per i numeri ID. Se hai numeri ID negativi penso che tu abbia problemi più grandi ... ti aspetti che il tuo pad sia del tipo "00000-1234"? o "-000001234"? Sinceramente data la domanda questa risposta funziona, è semplice, è pulita, è estensibile. Potrebbe non essere zfill ma se risponde alla domanda dovrebbe essere votato.
TastySlowCooker il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.