web-dev-qa-db-fra.com

Le meilleur moyen de compléter les zéros d'une chaîne

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?

1234
Faisal

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

Documentation de formatage de chaîne .

2034
Harley Holcombe

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'
317
Paul D. Eden

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 .

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

si vous voulez le contraire:

>>> '99'.ljust(5,'0')
'99000'
49
Victor Barrantes

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'
47
Cees Timmerman

str(n).zfill(width) fonctionnera avec strings, ints, floats ... 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'
36
Johnsyweb
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.

17
Peter Rowell

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

17
elad silver

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

Performance

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
_

La mise en oeuvre

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'
_
15
Aaron Hall

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

>>> a = 6340
>>> b = 90210
>>> print '%05d' % a
06340
>>> print '%05d' % b
90210
4
user221014
>>> width = 4
>>> x = 5
>>> print("%0*d" %(width,x))
>>> 00005

cela fonctionnera dans python 3.x

4
Saad

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

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.

2
Simon Steinberger

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