Comment ajouter des zéros à une chaîne?

1445

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?

Nicolas Gervais
la source

Réponses:

2394

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

Documentation sur le formatage des chaînes .

Harley Holcombe
la source
3
Code de format inconnu 'd' pour l'objet de type 'float'.
Cees Timmerman
7
Les commentaires python >= 2.6sont 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)?
Jason R. Coombs
4
Notez que vous n'avez pas besoin de numéroter vos chaînes de format: '{:03d} {:03d}'.format(1, 2)affecte implicitement les valeurs dans l'ordre.
Dragon
1
@ JasonR.Coombs: Je suppose que vous vouliez dire la printdéclaration, alors que ce devrait être une printfonction 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.
ShadowRanger
353

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'
Paul D. Eden
la source
123

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 .

Konrad Rudolph
la source
3
PEP 3101 n'indique pas que% est déprécié de quelque façon que ce soit.
zwirbeltier
@zwirbeltier PEP 3101 explique comment utiliser le format, c'est ce que je voulais dire.
Konrad Rudolph
4
Le "EDIT" indique toujours "... cette méthode de formatage est déconseillée ...".
zwirbeltier
1
@zwirbeltier Oui, et il est obsolète. Mais cela n'est pas directement indiqué dans le PEP. La documentation, cependant, dit d'utiliser à la formatplace, et les gens interprètent généralement cela comme une intention de déconseiller.
Konrad Rudolph
1
@LarsH Merci d'avoir trouvé cela. Ils sont donc très en retard sur le calendrier (Python 3.1 n'est pas dans le futur, c'est dans un passé lointain). Compte tenu de cela, je ne pense toujours pas que la réponse était trompeuse, mais pas rigoureusement mise à jour chaque fois que le calendrier de développement Python change dans une nouvelle direction arbitraire. Quoi qu'il en soit, cela m'a donné l'occasion de supprimer des éléments non pertinents et obsolètes de ma réponse.
Konrad Rudolph
63

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

si vous voulez le contraire:

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

str(n).zfill(width)fonctionnera avec strings, ints, floats ... et est compatible Python 2. x et 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'
Johnsyweb
la source
23

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

elad silver
la source
23

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.

Performance

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

la mise en oeuvre

Avec un peu de fouille, j'ai trouvé l'implémentation de la zfillmé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'

zfillappels pad(cette padfonction est également appelée par ljust, rjustet centeraussi 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, zfilldé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'
Aaron Hall
la source
pour les performances, y a-t-il des cas où les chaînes f sont meilleures, y compris les cas d'utilisation pour python2 vs python3? aussi, je pense que zfill n'est pas courant, cela aiderait votre réponse à avoir un lien vers les documents
elad silver
@eladsilver dépend de votre intention, en gardant à l'esprit le comportement avec +et -, et j'ai ajouté un lien vers les documents!
Aaron Hall
17
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.

Peter Rowell
la source
depuis 1980 ... vous êtes donc un programmeur de 60 ans ... pourriez-vous s'il vous plaît donner plus d'explications sur la façon dont "%0*d" % (width, x)est interprété par python?
Lee
15

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'
ruohola
la source
Merci pour le nouvel exemple de syntaxe. le caractère de remplissage 'x' sera: v = "A18"; s = f '{v: x> 8}' + "|"; ou s = v.ljust (8, "x") + "|";
Charlie
@Charlie 木匠 Était-ce une question pour moi ou juste une déclaration?
ruohola
juste une déclaration. testé un peu plus d'utilisation.
Charlie
4

Pour les codes postaux enregistrés sous forme d'entiers:

>>> a = 6340
>>> b = 90210
>>> print '%05d' % a
06340
>>> print '%05d' % b
90210
Acumenus
la source
1
Vous avez raison, et j'aime mieux votre suggestion avec zfill
3

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 zfillsolution a été la plus rapide.

Simon Steinberger
la source
1

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']
kmario23
la source
0

C'est ok aussi:

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

la sortie sera donc: "02:07:03"

zzfima
la source
-2

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:]
J Lacar
la source
1
Cela ne fonctionne que pour les nombres positifs. Cela devient un peu plus compliqué si vous voulez aussi des négatifs. Mais cette expression est bonne pour un travail rapide et sale, si cela ne vous dérange pas.
J Lacar
Je n'ai absolument aucune idée de la raison pour laquelle cela est sous-estimé. Si c'est parce que cela ne fonctionne pas assez bien sur les nombres négatifs, mais la raison primordiale pour laquelle on laisserait un tampon avec des zéros est pour les numéros d'identification. Si vous avez des numéros d'identification négatifs, je pense que vous avez de plus gros problèmes ... vous attendez-vous à ce que votre pad soit de la forme '00000-1234'? ou '-000001234'? Honnêtement, étant donné la question que cette réponse fonctionne, c'est simple, c'est propre, c'est extensible. Ce n'est peut-être pas zfill mais s'il répond à la question, il doit être voté positivement.
TastySlowCooker