Quelle est la manière la plus pythonique de remplir une chaîne numérique avec des zéros à gauche, c’est-à-dire si la chaîne numérique a une longueur spécifique?
Cordes:
>>> n = '4'
>>> print(n.zfill(3))
004
Et pour les chiffres:
>>> n = 4
>>> print('%03d' % n)
004
>>> print(format(n, '03')) # python >= 2.6
004
>>> print('{0:03d}'.format(n)) # python >= 2.6
004
>>> print('{foo:03d}'.format(foo=n)) # python >= 2.6
004
>>> print('{:03d}'.format(n)) # python >= 2.7 + python3
004
>>> print('{0:03d}'.format(n)) # python 3
004
>>> print(f'{n:03}') # python >= 3.6
004
Utilisez simplement la méthode rjust de l'objet string.
Cet exemple créera une chaîne de 10 caractères, complétée si nécessaire.
>>> t = 'test'
>>> t.rjust(10, '0')
>>> '000000test'
Outre zfill
, vous pouvez utiliser le formatage de chaîne général:
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'))
Documentation pour format de chaîne et chaînes f .
>>> '99'.zfill(5)
'00099'
>>> '99'.rjust(5,'0')
'00099'
si vous voulez le contraire:
>>> '99'.ljust(5,'0')
'99000'
Cela fonctionne à la fois dans Python 2 et Python 3:
>>> "{:0>2}".format("1") # Works for both numbers and strings.
'01'
>>> "{:02}".format(1) # Works only for numbers.
'01'
À utiliser avec Python 3.6+ à l'aide de chaînes de caractères f:
>>> i = 1
>>> f"{i:0>2}" # Works for both numbers and strings.
'01'
>>> f"{i:02}" # Works only for numbers.
'01'
str(n).zfill(width)
fonctionnera avec string
s, int
s, float
s ... et est Python 2 .x et 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'
width = 10
x = 5
print "%0*d" % (width, x)
> 0000000005
Voir la documentation d'impression pour tous les détails passionnants!
Mise à jour pour Python 3.x (7.5 ans plus tard)
Cette dernière ligne devrait maintenant être:
print("%0*d" % (width, x))
C'est à dire. print()
est maintenant une fonction, pas une instruction. Notez que je préfère toujours le style Old School printf()
parce que, IMNSHO, il se lit mieux, et parce que, euh, j'utilise cette annotation depuis janvier 1980. Quelque chose ... de vieux chiens ... quelque chose, quelque chose. .. nouvelles astuces.
Pour ceux qui sont venus ici pour comprendre et pas seulement une réponse rapide. Je fais ces surtout pour les chaînes de temps:
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" symboles à remplacer par les "2" caractères de remplissage, la valeur par défaut est un espace vide
Les symboles ">" allouent tous les 2 "0" à gauche de la chaîne
":" symbolise le format_spec
Quelle est la manière la plus pythonique de remplir une chaîne numérique avec des zéros à gauche, c’est-à-dire si la chaîne numérique a une longueur spécifique?
str.zfill
est spécifiquement conçu pour cela:
_>>> '1'.zfill(4)
'0001'
_
Notez qu'il est spécifiquement conçu pour gérer les chaînes numériques comme demandé et déplace un _+
_ ou _-
_ au début de la chaîne:
_>>> '+1'.zfill(4)
'+001'
>>> '-1'.zfill(4)
'-001'
_
Voici l'aide sur _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.
_
C'est aussi la plus performante des méthodes alternatives:
_>>> 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
_
Pour comparer au mieux les pommes avec les pommes avec la méthode _%
_ (notez qu’elle est en fait plus lente), qui pré-calculera autrement:
_>>> min(timeit.repeat(lambda: '1'.zfill(0 or 4)))
0.19728074967861176
>>> min(timeit.repeat(lambda: '%04d' % (0 or 1)))
0.2347015216946602
_
En creusant un peu, j'ai trouvé l'implémentation de la méthode zfill
dans 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;
}
_
Passons en revue ce code C.
Il analyse d'abord l'argument en fonction de sa position, ce qui signifie qu'il n'autorise pas les arguments de mots clés:
_>>> '1'.zfill(width=4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: zfill() takes no keyword arguments
_
Il vérifie ensuite si sa longueur est identique ou supérieure, auquel cas il renvoie la chaîne.
_>>> '1'.zfill(0)
'1'
_
zfill
appelle pad
(cette fonction pad
est également appelée par ljust
, rjust
et center
.). Cela copie le contenu dans une nouvelle chaîne et remplit le remplissage.
_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;
}
_
Après avoir appelé pad
, zfill
déplace tout point de départ précédent _+
_ ou _-
_ au début de la chaîne.
Notez que pour que la chaîne d'origine soit réellement numérique, il n'est pas nécessaire:
_>>> '+foo'.zfill(10)
'+000000foo'
>>> '-foo'.zfill(10)
'-000000foo'
_
Pour les codes postaux enregistrés sous forme d'entiers:
>>> a = 6340
>>> b = 90210
>>> print '%05d' % a
06340
>>> print '%05d' % b
90210
>>> width = 4
>>> x = 5
>>> print("%0*d" %(width,x))
>>> 00005
cela fonctionnera dans python 3.x
Lorsque vous utilisez >= Python 3.6
, le moyen le plus propre consiste à utiliser chaînes f avec formatage de chaîne :
>>> s = f"{1:08}" # inline
>>> s
'00000001'
>>> n = 1
>>> s = f"{n:08}" # int variable
>>> s
'00000001'
>>> c = "1"
>>> s = f"{c:0>8}" # str variable
>>> s
'00000001'
Comparaison rapide du temps:
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]
J'ai fait différents tests de différentes répétitions. Les différences ne sont pas énormes, mais dans tous les tests, la solution zfill
était la plus rapide.
Une autre approche consisterait à utiliser une compréhension de liste avec une vérification de condition pour les longueurs. Ci-dessous une démonstration:
# 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']