Как дополнить нули до строки?

1445

Как Pythonic способ дополнить числовую строку нулями слева, т.е. чтобы числовая строка имела определенную длину?

Николя Жерве
источник

Ответы:

2394

Строки:

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

Документация по форматированию строк .

Харли Холкомб
источник
3
Неизвестный код формата «d» для объекта типа «float».
Сис Тиммерман
7
Комментарии python >= 2.6неверны. Этот синтаксис не работает python >= 3. Вы можете изменить его на python < 3, но могу ли я предложить вместо этого всегда использовать круглые скобки и опускать комментарии вообще (поощряя рекомендуемое использование)?
Джейсон Р. Кумбс
4
Обратите внимание, что вам не нужно нумеровать строки формата: '{:03d} {:03d}'.format(1, 2)неявно присваивает значения по порядку.
Дракон
1
@ JasonR.Coombs: Я предполагаю, что вы имели в виду printутверждение, когда оно должно быть printфункцией на Python 3? Я редактировал в Parens; поскольку печатается только одна вещь, теперь она работает одинаково на Py2 и Py3.
ShadowRanger
353

Просто используйте метод rjust строкового объекта.

В этом примере будет сделана строка длиной 10 символов, дополненная по необходимости.

>>> t = 'test'
>>> t.rjust(10, '0')
>>> '000000test'
Пол Д. Иден
источник
124

Кроме того 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-строк .

Конрад Рудольф
источник
3
В PEP 3101 не указано, что% не рекомендуется.
zwirbeltier
@zwirbeltier PEP 3101 объясняет, как использовать формат, это то, что я имел в виду.
Конрад Рудольф
4
«РЕДАКТИРОВАТЬ» все еще заявляет «… этот метод форматирования устарел…».
zwirbeltier
1
@zwirbeltier Да, и это устарело. Но это прямо не указано в ОПТОСОЗ. Документация, однако, говорит, что formatвместо этого нужно использовать , и люди обычно интерпретируют это как намерение осудить.
Конрад Рудольф
1
@LarsH Спасибо, что нашли это. Таким образом, они сильно отстают от графика (Python 3.1 не в будущем, он в далеком прошлом). Учитывая это, я все еще не думаю, что ответ вводит в заблуждение, просто не строго обновляется каждый раз, когда график разработки Python меняется в новом, произвольном направлении. В любом случае, это дало мне возможность убрать из моего ответа неуместные и устаревшие вещи.
Конрад Рудольф
63

Для 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'
Сис Тиммерман
источник
56
>>> '99'.zfill(5)
'00099'
>>> '99'.rjust(5,'0')
'00099'

если хочешь наоборот:

>>> '99'.ljust(5,'0')
'99000'
Виктор Баррантес
источник
39

str(n).zfill(width)будет работать с strings, ints, floats ... и совместим с Python 2. x и 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'
Johnsyweb
источник
23

Для тех, кто пришел сюда, чтобы понять, а не просто быстрый ответ. Я делаю это специально для временных строк:

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" слева от строки

символы ":" формат_спец

серебро
источник
23

Какой самый питонный способ дополнить числовую строку нулями слева, то есть, чтобы числовая строка имела определенную длину?

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'
Аарон Холл
источник
для производительности, есть ли случаи, когда f строк лучше, включая сценарии использования для python2 против python3? Кроме того, я думаю, что поскольку zfill не является распространенным явлением, это поможет вашему ответу иметь ссылку на документы
elad silver
@eladsilver зависит от ваших намерений, учитывая поведение +и -, и я добавил ссылку на документы!
Аарон Холл
17
width = 10
x = 5
print "%0*d" % (width, x)
> 0000000005

Смотрите печатную документацию для всех интересных деталей!

Обновление для Python 3.x (7,5 лет спустя)

Эта последняя строка должна теперь быть:

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

Т.е. print()теперь это функция, а не утверждение. Обратите внимание, что я все еще предпочитаю printf()стиль старой школы, потому что, IMNSHO, он читается лучше, и потому что я использую эту запись с января 1980 года. Что-то ... старые собаки ... что-то ... что-то новое ...

Питер Роуэлл
источник
с 1980 года ... так вы 60-летний программист ... не могли бы вы дать больше объяснений о том, как "%0*d" % (width, x)интерпретируется Python?
Ли
15

При использовании 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'
Руохола
источник
Спасибо за новый пример синтаксиса. Заполнить символ 'x' будет: v = "A18"; s = f '{v: x> 8}' + "|"; или s = v.ljust (8, "x") + "|";
Чарли 木匠
@ Чарли 木匠 Это был вопрос для меня или просто утверждение?
Руохола
просто заявление. проверил еще немного использования.
Чарли 木匠
4

Для почтовых индексов, сохраняемых как целые числа:

>>> a = 6340
>>> b = 90210
>>> print '%05d' % a
06340
>>> print '%05d' % b
90210
Акаменус
источник
1
Вы правы, и мне все равно нравится ваше предложение с zfill
3

Быстрое сравнение сроков:

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решение было самым быстрым.

Саймон Стейнбергер
источник
1

Другой подход заключается в использовании списочного понимания с проверкой условий на длину. Ниже приведена демонстрация:

# 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']
kmario23
источник
0

Это тоже хорошо:

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

поэтому вывод будет: «02:07:03»

zzfima
источник
-2

Вы также можете повторить «0», добавить его str(n)и получить самый правый срез ширины. Быстрое и грязное маленькое выражение.

def pad_left(n, width, pad="0"):
    return ((pad * width) + str(n))[-width:]
Дж. Лакар
источник
1
Это работает только для положительных чисел, хотя. Это становится немного сложнее, если вы тоже хотите негативы. Но это выражение хорошо для быстрой и грязной работы, если вы не возражаете против такого рода вещей.
Дж. Лакар
Я понятия не имею, почему это отрицательно. Если это потому, что он не работает на отрицательных числах достаточно справедливо, но подавляющая причина, по которой можно оставить заполнение нулями, это номера идентификаторов. Если у вас отрицательные идентификационные номера, я думаю, что у вас есть большие проблемы ... Вы ожидаете, что ваш планшет будет иметь форму "00000-1234"? или «-000001234»? Честно говоря, учитывая вопрос, этот ответ работает, он простой, чистый, расширяемый. Это может быть не zfill, но если он отвечает на вопрос, за него следует проголосовать.
TastySlowCooker