¿Cómo rellenar ceros a una cadena?

1445

¿Cuál es una forma pitónica de rellenar una cadena numérica con ceros a la izquierda, es decir, que la cadena numérica tiene una longitud específica?

Nicolas Gervais
fuente

Respuestas:

2394

Instrumentos de cuerda:

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

Y para números:

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

Documentación de formato de cadena .

Harley Holcombe
fuente
3
Código de formato desconocido 'd' para el objeto de tipo 'float'.
Cees Timmerman
77
Los comentarios python >= 2.6son incorrectos Esa sintaxis no funciona python >= 3. Puede cambiarlo a python < 3, pero ¿puedo sugerirle que siempre use paréntesis y omita los comentarios por completo (fomentando el uso recomendado)?
Jason R. Coombs
44
Tenga en cuenta que no necesita numerar sus cadenas de formato: '{:03d} {:03d}'.format(1, 2)asigna implícitamente los valores en orden.
Dragon
1
@ JasonR.Coombs: Supongo que te refieres a la printdeclaración, ¿cuándo debería ser una printfunción en Python 3? Edité en los parens Como solo se está imprimiendo una cosa, ahora funciona de manera idéntica en Py2 y Py3.
ShadowRanger
353

Solo usa el método rjust del objeto string.

Este ejemplo hará una cadena de 10 caracteres de largo, rellenando según sea necesario.

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

Además zfill, puede usar el formato de cadena general:

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

La documentación para el formato de cadenas y f-cuerdas .

Konrad Rudolph
fuente
3
PEP 3101 no indica que% está en desuso de ninguna manera.
zwirbeltier
@zwirbeltier PEP 3101 explica cómo usar el formato, es lo que quise decir.
Konrad Rudolph
44
El "EDITAR" aún dice "... este método de formateo está en desuso ...".
zwirbeltier
1
@zwirbeltier Sí, y está en desuso. Pero esto no se afirma directamente en el PEP. La documentación, sin embargo, dice que se debe usar formaten su lugar, y las personas generalmente interpretan esto como una intención de desaprobar.
Konrad Rudolph
1
@LarsH Gracias por encontrar esto. Por lo tanto, están muy retrasados ​​(Python 3.1 no está en el futuro, está en el pasado distante). Dado eso, todavía no creo que la respuesta fuera engañosa, simplemente no se actualiza estrictamente cada vez que el programa de desarrollo de Python cambia en una nueva dirección arbitraria. De todos modos, esto me dio la oportunidad de eliminar algunas cosas irrelevantes y obsoletas de mi respuesta.
Konrad Rudolph
63

Para Python 3.6+ usando f-strings:

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

Para 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'
Cees Timmerman
fuente
56
>>> '99'.zfill(5)
'00099'
>>> '99'.rjust(5,'0')
'00099'

si quieres lo contrario:

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

str(n).zfill(width)funcionará con strings, ints, floats ... y es Python 2. x y 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'
Johnsyweb
fuente
23

Para los que vinieron aquí a entender y no solo una respuesta rápida. Hago esto especialmente para cadenas de tiempo:

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

Símbolos "0" qué reemplazar con los caracteres de relleno "2", el valor predeterminado es un espacio vacío

Los símbolos ">" alinean todos los caracteres 2 "0" a la izquierda de la cadena

":" simboliza la especificación_formato

elad plata
fuente
23

¿Cuál es la forma más pitónica de rellenar una cadena numérica con ceros a la izquierda, es decir, que la cadena numérica tiene una longitud específica?

str.zfill está específicamente destinado a hacer esto:

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

Tenga en cuenta que está específicamente diseñado para manejar cadenas numéricas según lo solicitado, y mueve +ao -al comienzo de la cadena:

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

Aquí está la ayuda sobre 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.

Actuación

Este también es el más alternativo de los métodos alternativos:

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

Para comparar mejor las manzanas con las manzanas para el %método (tenga en cuenta que en realidad es más lento), que de lo contrario calculará previamente:

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

Implementación

Con un poco de excavación, encontré la implementación del zfillmétodo en 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;
}

Veamos este código C.

Primero analiza el argumento posicionalmente, lo que significa que no permite argumentos de palabras clave:

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

Luego comprueba si tiene la misma longitud o más, en cuyo caso devuelve la cadena.

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

zfillllamadas pad(esta padfunción también es llamada por ljust, rjusty centerasí). Básicamente, esto copia el contenido en una nueva cadena y rellena el relleno.

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

Después de llamar pad, zfillmueve cualquier original anterior +o -al comienzo de la cadena.

Tenga en cuenta que para que la cadena original sea realmente numérica no se requiere:

>>> '+foo'.zfill(10)
'+000000foo'
>>> '-foo'.zfill(10)
'-000000foo'
Aaron Hall
fuente
para el rendimiento, ¿hay casos en los que las cadenas f son mejores, incluidos los casos de uso para python2 frente a python3? Además, creo que como zfill no es común, ayudaría a su respuesta tener un enlace a los documentos
elad silver
@eladsilver depende de tu intención, teniendo en cuenta el comportamiento con +y -, ¡y agregué un enlace a los documentos!
Aaron Hall
17
width = 10
x = 5
print "%0*d" % (width, x)
> 0000000005

¡Vea la documentación impresa para todos los detalles interesantes!

Actualización para Python 3.x (7.5 años después)

Esa última línea ahora debería ser:

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

Es decir, print()ahora es una función, no una declaración. Tenga en cuenta que todavía prefiero el printf()estilo Old School porque, IMNSHO, se lee mejor, y porque, um, he estado usando esa notación desde enero de 1980. Algo ... perros viejos ... algo algo ... nuevos trucos.

Peter Rowell
fuente
desde 1980 ... así que eres un programador de 60 años ... ¿podrías dar más explicaciones sobre cómo "%0*d" % (width, x)es interpretado por python?
Lee
15

Cuando se usa Python >= 3.6, la forma más limpia es usar cadenas f con formato de cadena :

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

Preferiría formatear con un int, ya que solo entonces el signo se maneja correctamente:

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

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

>>> f"{'-1':0>8}"
'000000-1'
ruohola
fuente
Gracias por el nuevo ejemplo de sintaxis. llenar char 'x' será: v = "A18"; s = f '{v: x> 8}' + "|"; o s = v.ljust (8, "x") + "|";
Charlie 木匠
@Charlie that ¿Fue una pregunta para mí o solo una declaración?
Ruohola
Solo una declaración. probó un poco más de uso.
Charlie
4

Para códigos postales guardados como enteros:

>>> a = 6340
>>> b = 90210
>>> print '%05d' % a
06340
>>> print '%05d' % b
90210
Acumenus
fuente
1
Estás en lo correcto, y me gusta su sugerencia con zfill mejor de todos modos
3

Comparación rápida de tiempos:

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]

He hecho diferentes pruebas de diferentes repeticiones. Las diferencias no son enormes, pero en todas las pruebas, la zfillsolución fue más rápida.

Simon Steinberger
fuente
1

Otro enfoque sería utilizar una lista de comprensión con una condición de verificación de longitudes. A continuación se muestra una demostración:

# 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
fuente
0

Está bien también:

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

entonces la salida será: "02:07:03"

zzfima
fuente
-2

También puede repetir "0", anteponerlo str(n)y obtener el corte de ancho más a la derecha. Pequeña expresión rápida y sucia.

def pad_left(n, width, pad="0"):
    return ((pad * width) + str(n))[-width:]
J Lacar
fuente
1
Sin embargo, esto solo funciona para números positivos. Se vuelve un poco más complicado si quieres negativos también. Pero esta expresión es buena para el trabajo rápido y sucio, si no te importa ese tipo de cosas.
J Lacar
No tengo ni idea de por qué esto se rechaza. Si es porque no funciona con números negativos lo suficientemente justos, pero la razón abrumadora por la que uno dejaría el pad con ceros es para los números de identificación. Si tiene números de identificación negativos, creo que tiene mayores problemas ... ¿espera que su bloc tenga la forma '00000-1234'? o '-000001234'? Honestamente, dada la pregunta, esta respuesta funciona, es simple, está limpia, es extensible. Puede que no sea zfill pero si responde a la pregunta, debería ser votado a favor.
TastySlowCooker