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ść?
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:
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
python >= 2.6
są 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)?
'{:03d} {:03d}'.format(1, 2)
domyślnie przypisuje wartości w kolejności.
print
stwierdzenie, kiedy powinna to być print
funkcja w Pythonie 3? Redagowałem w parens; ponieważ drukowana jest tylko jedna rzecz, działa teraz identycznie na Py2 i Py3.
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'
Poza tym zfill
moż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 .
format
zamiast tego, a ludzie ogólnie interpretują to jako zamiar zaniechania.
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'
>>> '99'.zfill(5)
'00099'
>>> '99'.rjust(5,'0')
'00099'
jeśli chcesz czegoś przeciwnego:
>>> '99'.ljust(5,'0')
'99000'
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
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.
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
Przy odrobinie kopania znalazłem implementację zfill
metody 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'
zfill
Połączenia pad
(ta pad
funkcja nazywa się także ljust
, rjust
i center
jak 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
, zfill
porusza 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'
+
i -
, i dodałem link do docs!
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.
"%0*d" % (width, x)
interpretuje go Python?
W przypadku Pythona >= 3.6
najczystszym 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'
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 zfill
rozwiązanie było najszybsze.
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']
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:]