Qu'est-ce qu'une façon Pythonique de remplir une chaîne numérique avec des zéros à gauche, c'est-à-dire que la chaîne numérique a une longueur spécifique?
Qu'est-ce qu'une façon Pythonique de remplir une chaîne numérique avec des zéros à gauche, c'est-à-dire que la chaîne numérique a une longueur spécifique?
Réponses:
Cordes:
>>> n = '4'
>>> print(n.zfill(3))
004
Et pour les chiffres:
>>> 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
sont incorrects. Cette syntaxe ne fonctionne pas python >= 3
. Vous pouvez le changer en python < 3
, mais puis-je suggérer à la place de toujours utiliser des parenthèses et d'omettre complètement les commentaires (encourageant l'utilisation recommandée)?
'{:03d} {:03d}'.format(1, 2)
affecte implicitement les valeurs dans l'ordre.
print
déclaration, alors que ce devrait être une print
fonction sur Python 3? J'ai édité dans les parens; puisqu'une seule chose est en cours d'impression, elle fonctionne de manière identique sur Py2 et Py3.
Utilisez simplement la méthode rjust de l'objet chaîne.
Cet exemple crée une chaîne de 10 caractères, complétant au besoin.
>>> t = 'test'
>>> t.rjust(10, '0')
>>> '000000test'
En outre zfill
, vous pouvez utiliser la mise en forme générale des chaînes:
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 la mise en forme de chaîne et f-cordes .
format
place, et les gens interprètent généralement cela comme une intention de déconseiller.
Pour Python 3.6+ utilisant des chaînes f:
>>> i = 1
>>> f"{i:0>2}" # Works for both numbers and strings.
'01'
>>> f"{i:02}" # Works only for numbers.
'01'
Pour Python 2 à 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 vous voulez le contraire:
>>> '99'.ljust(5,'0')
'99000'
Pour ceux qui sont venus ici pour comprendre et pas seulement une réponse rapide. Je fais cela en particulier 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" symbolise ce qu'il faut remplacer par les caractères de remplissage "2", la valeur par défaut est un espace vide
Les symboles ">" alignent tous les 2 "0" caractères à gauche de la chaîne
":" symbolise le format_spec
Quelle est la façon la plus pythonique de remplir une chaîne numérique avec des zéros à gauche, c'est-à-dire que la chaîne numérique a une longueur spécifique?
str.zfill
est spécifiquement destiné à ce faire:
>>> '1'.zfill(4)
'0001'
Notez qu'il est spécifiquement destiné à gérer les chaînes numériques comme demandé, et déplace un +
ou -
vers le 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 également 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 aux pommes pour 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
Avec un peu de fouille, j'ai trouvé l'implémentation de la zfill
méthode 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 positionnellement, 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 c'est la même longueur ou plus, auquel cas il retourne la chaîne.
>>> '1'.zfill(0)
'1'
zfill
appels pad
(cette pad
fonction est également appelée par ljust
, rjust
et center
aussi bien). Cela copie essentiellement 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 l'appel pad
, zfill
déplace n'importe quel 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'
+
et -
, et j'ai ajouté un lien vers les documents!
width = 10
x = 5
print "%0*d" % (width, x)
> 0000000005
Voir la documentation imprimée 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()
maintenant une fonction, pas une déclaration. Notez que je préfère toujours le printf()
style Old School parce que, IMNSHO, il se lit mieux et parce que, euh, j'utilise cette notation depuis janvier 1980. Quelque chose ... de vieux chiens .. quelque chose de ... de nouveaux trucs.
"%0*d" % (width, x)
est interprété par python?
Lorsque vous utilisez Python >= 3.6
, la manière la plus propre consiste à utiliser des chaînes f avec un formatage de chaîne :
>>> 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'
Je préfère formater avec un int
, car ce n'est qu'alors que le signe est traité correctement:
>>> f"{-1:08}"
'-0000001'
>>> f"{1:+08}"
'+0000001'
>>> f"{'-1':0>8}"
'000000-1'
Comparaison rapide du timing:
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 zfill
solution a été la plus rapide.
Une autre approche serait d'utiliser une compréhension de liste avec une condition vérifiant les longueurs. Voici 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']
Vous pouvez également répéter «0», l'ajouter au début str(n)
et obtenir la tranche de largeur la plus à droite. Petite expression rapide et sale.
def pad_left(n, width, pad="0"):
return ((pad * width) + str(n))[-width:]