Как Pythonic способ дополнить числовую строку нулями слева, т.е. чтобы числовая строка имела определенную длину?
Как Pythonic способ дополнить числовую строку нулями слева, т.е. чтобы числовая строка имела определенную длину?
Ответы:
Строки:
>>> n = '4'
>>> print(n.zfill(3))
004
И для номеров:
>>> 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
неверны. Этот синтаксис не работает python >= 3
. Вы можете изменить его на python < 3
, но могу ли я предложить вместо этого всегда использовать круглые скобки и опускать комментарии вообще (поощряя рекомендуемое использование)?
'{:03d} {:03d}'.format(1, 2)
неявно присваивает значения по порядку.
print
утверждение, когда оно должно быть print
функцией на Python 3? Я редактировал в Parens; поскольку печатается только одна вещь, теперь она работает одинаково на Py2 и Py3.
Просто используйте метод rjust строкового объекта.
В этом примере будет сделана строка длиной 10 символов, дополненная по необходимости.
>>> t = 'test'
>>> t.rjust(10, '0')
>>> '000000test'
Кроме того zfill
, вы можете использовать общее форматирование строки:
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'))
Документация для форматирования строк и f-строк .
format
вместо этого нужно использовать , и люди обычно интерпретируют это как намерение осудить.
Для Python 3.6+ с использованием f-строк:
>>> i = 1
>>> f"{i:0>2}" # Works for both numbers and strings.
'01'
>>> f"{i:02}" # Works only for numbers.
'01'
Для Python 2 до 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'
если хочешь наоборот:
>>> '99'.ljust(5,'0')
'99000'
Для тех, кто пришел сюда, чтобы понять, а не просто быстрый ответ. Я делаю это специально для временных строк:
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», которые следует заменить символами «2», по умолчанию это пробел
Символы ">" выравнивают все 2 символа "0" слева от строки
символы ":" формат_спец
Какой самый питонный способ дополнить числовую строку нулями слева, то есть, чтобы числовая строка имела определенную длину?
str.zfill
специально предназначен для этого:
>>> '1'.zfill(4)
'0001'
Обратите внимание, что он специально предназначен для обработки числовых строк по запросу и перемещает a +
или -
в начало строки:
>>> '+1'.zfill(4)
'+001'
>>> '-1'.zfill(4)
'-001'
Вот помощь по 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.
Это также самый эффективный из альтернативных методов:
>>> 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
Чтобы лучше сравнить яблоки с яблоками по %
методу (обратите внимание, что это на самом деле медленнее), который в противном случае будет предварительно рассчитать:
>>> min(timeit.repeat(lambda: '1'.zfill(0 or 4)))
0.19728074967861176
>>> min(timeit.repeat(lambda: '%04d' % (0 or 1)))
0.2347015216946602
Немного покопавшись, я нашел реализацию zfill
метода в 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;
}
Давайте пройдемся по этому C-коду.
Сначала он анализирует аргумент позиционно, то есть он не допускает аргументы с ключевыми словами:
>>> '1'.zfill(width=4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: zfill() takes no keyword arguments
Затем он проверяет, имеет ли он ту же длину или больше, и в этом случае он возвращает строку.
>>> '1'.zfill(0)
'1'
zfill
вызовов pad
(эта pad
функция также вызывается ljust
, rjust
и center
а). Это в основном копирует содержимое в новую строку и заполняет отступы.
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;
}
После вызова pad
, zfill
движется любой первоначально предшествующее +
или -
в начале строки.
Обратите внимание, что для того, чтобы исходная строка была фактически числовой, не требуется:
>>> '+foo'.zfill(10)
'+000000foo'
>>> '-foo'.zfill(10)
'-000000foo'
+
и -
, и я добавил ссылку на документы!
width = 10
x = 5
print "%0*d" % (width, x)
> 0000000005
Смотрите печатную документацию для всех интересных деталей!
Обновление для Python 3.x (7,5 лет спустя)
Эта последняя строка должна теперь быть:
print("%0*d" % (width, x))
Т.е. print()
теперь это функция, а не утверждение. Обратите внимание, что я все еще предпочитаю printf()
стиль старой школы, потому что, IMNSHO, он читается лучше, и потому что я использую эту запись с января 1980 года. Что-то ... старые собаки ... что-то ... что-то новое ...
"%0*d" % (width, x)
интерпретируется Python?
При использовании Python >= 3.6
самый простой способ - использовать f-строки с форматированием строк :
>>> 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'
Я бы предпочел форматирование с int
, так как только тогда знак обрабатывается правильно:
>>> f"{-1:08}"
'-0000001'
>>> f"{1:+08}"
'+0000001'
>>> f"{'-1':0>8}"
'000000-1'
Быстрое сравнение сроков:
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]
Я сделал разные тесты разных повторений. Различия невелики, но во всех тестах zfill
решение было самым быстрым.
Другой подход заключается в использовании списочного понимания с проверкой условий на длину. Ниже приведена демонстрация:
# 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», добавить его str(n)
и получить самый правый срез ширины. Быстрое и грязное маленькое выражение.
def pad_left(n, width, pad="0"):
return ((pad * width) + str(n))[-width:]