¿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?
¿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?
Respuestas:
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
python >= 2.6
son 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)?
'{:03d} {:03d}'.format(1, 2)
asigna implícitamente los valores en orden.
print
declaración, ¿cuándo debería ser una print
funció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.
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'
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 .
format
en su lugar, y las personas generalmente interpretan esto como una intención de desaprobar.
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'
>>> '99'.zfill(5)
'00099'
>>> '99'.rjust(5,'0')
'00099'
si quieres lo contrario:
>>> '99'.ljust(5,'0')
'99000'
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
¿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.
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
Con un poco de excavación, encontré la implementación del zfill
mé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'
zfill
llamadas pad
(esta pad
función también es llamada por ljust
, rjust
y center
así). 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
, zfill
mueve 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'
+
y -
, ¡y agregué un enlace a los documentos!
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.
"%0*d" % (width, x)
es interpretado por python?
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'
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 zfill
solución fue más rápida.
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']
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:]