Jak wstawić zera do łańcucha?


1442

Jaki jest Pythoński sposób wypełniania łańcucha numerycznego zerami po lewej stronie, tj. Aby łańcuch liczbowy miał określoną długość?

Odpowiedzi:


2390

Smyczki:

>>> n = '4'
>>> print(n.zfill(3))
004

A dla liczb:

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

Dokumentacja formatowania łańcucha .


3
Nieznany kod formatu „d” dla obiektu typu „float”.
Cees Timmerman

7
Komentarze python >= 2.6są niepoprawne. Ta składnia nie działa python >= 3. Możesz to zmienić python < 3, ale czy mogę zasugerować, aby zawsze używać nawiasów i całkowicie pomijać komentarze (zachęcając do zalecanego używania)?
Jason R. Coombs

4
Zauważ, że nie musisz numerować ciągów formatu: '{:03d} {:03d}'.format(1, 2)domyślnie przypisuje wartości w kolejności.
Dragon

1
@ JasonR.Coombs: Zakładam, że miałeś na myśli printstwierdzenie, kiedy powinna to być printfunkcja w Pythonie 3? Redagowałem w parens; ponieważ drukowana jest tylko jedna rzecz, działa teraz identycznie na Py2 i Py3.
ShadowRanger


353

Wystarczy użyć metody rjust obiektu string.

Ten przykład utworzy łańcuch o długości 10 znaków, w razie potrzeby dopełniający.

>>> t = 'test'
>>> t.rjust(10, '0')
>>> '000000test'

123

Poza tym zfillmożesz użyć ogólnego formatowania ciągów:

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

Dokumentacja dotycząca formatowania i f-stringów .


3
PEP 3101 nie stwierdza, że% jest w jakikolwiek sposób przestarzałe.
zwirbeltier

@zwirbeltier PEP 3101 wyjaśnia, jak używać formatu, o to mi chodziło.
Konrad Rudolph

4
„EDIT” nadal stwierdza „… ta metoda formatowania jest przestarzała…”.
zwirbeltier

1
@zwirbeltier Tak, i jest przestarzałe. Ale nie jest to bezpośrednio określone w PEP. Dokumentacja mówi jednak, aby używać formatzamiast tego, a ludzie ogólnie interpretują to jako zamiar zaniechania.
Konrad Rudolph

1
@LarsH Dziękujemy za znalezienie tego. Są więc poważnie opóźnione w realizacji (Python 3.1 nie jest w przyszłości, jest w odległej przeszłości). Biorąc to pod uwagę, nadal nie sądzę, aby odpowiedź była myląca, ale nie była rygorystycznie aktualizowana za każdym razem, gdy harmonogram rozwoju Pythona zmieniał się w nowym, arbitralnym kierunku. W każdym razie dało mi to możliwość usunięcia z mojej odpowiedzi niektórych nieistotnych i nieaktualnych informacji.
Konrad Rudolph

62

W przypadku języka Python 3.6+ używającego ciągów F:

>>> i = 1
>>> f"{i:0>2}"  # Works for both numbers and strings.
'01'
>>> f"{i:02}"  # Works only for numbers.
'01'

W przypadku Python 2 do Python 3.5:

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

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

jeśli chcesz czegoś przeciwnego:

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

39

str(n).zfill(width)będzie działać z strings, ints, floats ... i jest kompatybilny z Python 2. xi 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'

23

Dla tych, którzy przybyli tutaj, aby zrozumieć, a nie tylko szybką odpowiedź. Robię to szczególnie dla ciągów czasowych:

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

Symbole „0”, które należy zastąpić znakami wypełniającymi „2”, domyślnie jest to puste miejsce

Symbole „>” wyrównują wszystkie 2 „0” po lewej stronie ciągu

„:” symbole format_spec


23

Jaki jest najbardziej pytonowy sposób wypełniania łańcucha numerycznego zerami po lewej stronie, tj. Aby łańcuch liczbowy miał określoną długość?

str.zfill jest specjalnie przeznaczony do tego:

>>> '1'.zfill(4)
'0001'

Zauważ, że jest specjalnie przeznaczony do obsługi ciągów liczbowych zgodnie z żądaniem i przesuwa a +lub -na początek ciągu:

>>> '+1'.zfill(4)
'+001'
>>> '-1'.zfill(4)
'-001'

Oto pomoc dotycząca 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.

Wydajność

Jest to również najbardziej wydajna metoda alternatywna:

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

Aby najlepiej porównać jabłka z jabłkami dla tej %metody (zwróć uwagę, że jest ona wolniejsza), która w przeciwnym razie wstępnie obliczy:

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

Realizacja

Przy odrobinie kopania znalazłem implementację zfillmetody w 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;
}

Przejdźmy przez ten kod C.

Najpierw analizuje argument w pozycji, co oznacza, że ​​nie dopuszcza argumentów słów kluczowych:

>>> '1'.zfill(width=4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: zfill() takes no keyword arguments

Następnie sprawdza, czy ma taką samą długość lub dłużej, w którym to przypadku zwraca ciąg.

>>> '1'.zfill(0)
'1'

zfillPołączenia pad(ta padfunkcja nazywa się także ljust, rjusti centerjak dobrze). To w zasadzie kopiuje zawartość do nowego ciągu i wypełnia wypełnienie.

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

Po wywołaniu pad, zfillporusza każdy pierwotnie poprzedzający +lub -na początku łańcucha.

Pamiętaj, że oryginalny ciąg znaków, który ma być numeryczny, nie jest wymagany:

>>> '+foo'.zfill(10)
'+000000foo'
>>> '-foo'.zfill(10)
'-000000foo'

dla wydajności, czy istnieją przypadki, w których f ciągi są lepsze, w tym przypadki użycia dla python2 vs python3? również, myślę, że ponieważ zfill nie jest powszechny, pomogłoby ci to w uzyskaniu linku do dokumentów
elad silver

@eladsilver zależy od intencji, mając na uwadze zachowanie się +i -, i dodałem link do docs!
Aaron Hall

17
width = 10
x = 5
print "%0*d" % (width, x)
> 0000000005

Zobacz ekscytujące szczegóły w dokumentacji drukowanej!

Aktualizacja dla Python 3.x (7,5 roku później)

Ostatnia linia powinna być teraz:

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

Tj. print()Jest teraz funkcją, a nie instrukcją. Zauważ, że nadal wolę printf()styl Old School , ponieważ, IMNSHO, czyta lepiej, i ponieważ, um, używam tego zapisu od stycznia 1980 roku. Coś… stare psy… coś… nowe sztuczki.


od 1980 roku ... więc jesteś 60-letnim programistą ... czy mógłbyś podać więcej wyjaśnień na temat tego, jak "%0*d" % (width, x)interpretuje go Python?
Lee,

15

W przypadku Pythona >= 3.6najczystszym sposobem jest użycie ciągów F z formatowaniem ciągów :

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

Wolałbym formatować za pomocą int, ponieważ tylko wtedy znak jest obsługiwany poprawnie:

>>> f"{-1:08}"
'-0000001'

>>> f"{1:+08}"
'+0000001'

>>> f"{'-1':0>8}"
'000000-1'

Dzięki za nowy przykład składni. fill char 'x' będzie: v = "A18"; s = f '{v: x> 8}' + "|"; lub s = v.ljust (8, „x”) + „|”;
Charlie

@Charlie 木匠 Czy to było dla mnie pytanie, czy tylko oświadczenie?
ruohola

tylko oświadczenie. przetestowałem trochę więcej użycia.
Charlie

4

W przypadku kodów pocztowych zapisanych jako liczby całkowite:

>>> a = 6340
>>> b = 90210
>>> print '%05d' % a
06340
>>> print '%05d' % b
90210

1
Masz rację, a mimo to podoba mi się twoja sugestia z zfill

3

Szybkie porównanie czasów:

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]

Przeprowadziłem różne testy różnych powtórzeń. Różnice nie są ogromne, ale we wszystkich testach zfillrozwiązanie było najszybsze.


1

Innym podejściem byłoby użycie zrozumienia listy z warunkiem sprawdzania długości. Poniżej znajduje się demonstracja:

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

0

Jest też w porządku:

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

więc wynikiem będzie: „02:07:03”


-2

Możesz także powtórzyć „0”, wstawić go do str(n)poprzedniej pozycji i uzyskać plasterek o skrajnej prawej szerokości. Szybki i brudny mały wyraz.

def pad_left(n, width, pad="0"):
    return ((pad * width) + str(n))[-width:]

1
Działa to jednak tylko dla liczb dodatnich. To staje się trochę bardziej skomplikowane, jeśli chcesz również negatywów. Ale to wyrażenie jest dobre do szybkiej i brudnej pracy, jeśli nie przeszkadza ci tego rodzaju rzecz.
J Lacar,

Absolutnie nie mam pojęcia, dlaczego jest to przegłosowane. Jeśli to dlatego, że nie działa wystarczająco dobrze na liczbach ujemnych, ale przytłaczający powód, dla którego zostawiono pad zerami, to liczby id. Jeśli masz ujemne numery identyfikacyjne, myślę, że masz większe problemy ... czy spodziewasz się, że Twój pad będzie miał postać „00000-1234”? lub „-000001234”? Szczerze mówiąc, biorąc pod uwagę pytanie, ta odpowiedź działa, jest prosta, czysta, rozszerzalna. To może nie być zfill, ale jeśli odpowie na pytanie, należy je ocenić.
TastySlowCooker
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.