Question

Quel est le moyen le 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?

Était-ce utile?

La solution

Chaînes:

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

Documentation sur le formatage des chaînes .

Autres conseils

Utilisez simplement la méthode rjust de l'objet chaîne.

Cet exemple créera une chaîne de 10 caractères, complétée au besoin.

>>> 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 sur les formatage de chaîne et f-strings .

>>> '99'.zfill(5)
'00099'
>>> '99'.rjust(5,'0')
'00099'

si vous voulez le contraire:

>>> '99'.ljust(5,'0')
'99000'

Fonctionne à la fois en Python 2 et en Python 3:

>>> "{:0>2}".format("1")  # Works for both numbers and strings.
'01'
>>> "{:02}".format(1)  # Only works for numbers.
'01'

str(n).zfill(width) fonctionnera avec string s, int s, float s ... et correspond à 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

Consultez la documentation imprimée pour connaître tous les détails intéressants!

Mise à jour pour Python 3.x (7.5 ans plus tard)

Cette dernière ligne devrait maintenant être:

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

I.e. print() est maintenant une fonction, pas une déclaration. 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 de 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 le " 2 " les caractères de remplissage, la valeur par défaut est un espace vide

     

& "; > &"; les symboles allient tous les 2 " 0 " caractère à gauche de la chaîne

     

" " symboles le format_spec

  

Quel est le moyen le plus pythonique de compléter 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écialement 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 %:

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

Performances

C’est également la méthode la plus performante parmi les 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 zfill (notez que c'est en fait plus lent), sinon, vous calculerez à l'avance:

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

Mise en œuvre

En creusant un peu, j'ai trouvé l'implémentation de la méthode Objects/stringlib/transmogrify.h dans pad :

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

Parcourons ce code C.

Il analyse d'abord l'argument en 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'

ljust appelle rjust (cette fonction center est également appelée par <=>, <=> et <=> également). 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é <=>, <=> déplace tout ce qui précède <=> 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

Comparaison des délais rapides:

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 répétitions différentes. Les différences ne sont pas énormes, mais dans tous les tests, la zfill solution était la plus rapide.

Lorsque vous utilisez >= Python 3.6, le moyen le plus simple consiste à utiliser f- des chaînes avec le formatage des chaînes :

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

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

Vous pouvez également répéter & "; 0 &"; ajouter le préfixe à 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:]
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top