Frage

Was ist die pythonischste Art, eine numerische Zeichenfolge links mit Nullen aufzufüllen, d. h. damit die numerische Zeichenfolge eine bestimmte Länge hat?

War es hilfreich?

Lösung

Strings:

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

Und für Zahlen:

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

String Formatierung Dokumentation .

Andere Tipps

Verwenden Sie einfach die rjust Methode des String-Objekt.

In diesem Beispiel wird eine Kette von 10 Zeichen lang, Polsterung macht als nötig.

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

Neben zfill können Sie allgemeine String-Formatierung:

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

Dokumentation für String Formatierung und f-Strings .

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

, wenn Sie wollen das Gegenteil:

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

funktioniert in beiden Python 2 und Python 3:

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

str(n).zfill(width) mit strings, ints, floats arbeiten ... und ist Python 2. x und 3. x kompatibel:

>>> n = 3
>>> str(n).zfill(5)
'00003'
>>> n = '3'
>>> str(n).zfill(5)
'00003'
>>> n = '3.0'
>>> str(n).zfill(5)
'003.0'
width = 10
x = 5
print "%0*d" % (width, x)
> 0000000005

Sehen Sie die Druckunterlagen für alle spannenden Details!

Update für Python 3.x (7,5 Jahre später)

Das letzte Zeile soll jetzt sein:

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

d. print() ist jetzt eine Funktion, keine Aussage. Beachten Sie, dass ich immer noch die alte Schule printf() Stil bevorzugen, weil IMNSHO, ist es besser liest, und weil, äh, ich habe seit Januar, dass Notation wurde mit 1980 etwas ... alte Hunde .. etwas etwas ... neue Tricks .

Für diejenigen, die hierher kamen, zu verstehen und nicht nur eine schnelle Antwort. Ich mache diese besonders für die Zeitzeichenketten:

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“ Symbole, was mit den „2“ padding Zeichen zu ersetzen, der Standardwert ist ein leerer Raum

     

">" Symbole allign alle 2 "0" Zeichen links von der Zeichenfolge

     

":" Symbole der format_spec

Was ist die pythonischste Methode, um eine numerische Zeichenfolge links mit Nullen aufzufüllen, d. h. damit die numerische Zeichenfolge eine bestimmte Länge hat?

str.zfill ist speziell dazu gedacht:

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

Beachten Sie, dass es speziell dazu gedacht ist, numerische Zeichenfolgen wie angefordert zu verarbeiten und a zu verschieben + oder - zum Anfang der Zeichenfolge:

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

Hier ist die Hilfe 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.

Leistung

Dies ist auch die leistungsstärkste alternative Methode:

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

Um Äpfel am besten mit Äpfeln zu vergleichen % Methode (beachten Sie, dass sie tatsächlich langsamer ist), die ansonsten Folgendes vorberechnet:

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

Implementierung

Mit ein wenig Recherche habe ich die Implementierung gefunden zfill Methode in 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;
}

Lassen Sie uns diesen C-Code durchgehen.

Das Argument wird zunächst nach der Position analysiert, was bedeutet, dass keine Schlüsselwortargumente zulässig sind:

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

Anschließend wird geprüft, ob die Länge gleich oder länger ist. In diesem Fall wird die Zeichenfolge zurückgegeben.

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

zfill Anrufe pad (Das pad Funktion wird auch aufgerufen von ljust, rjust, Und center sowie).Dadurch wird im Grunde der Inhalt in eine neue Zeichenfolge kopiert und die Auffüllung ausgefüllt.

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

Nach dem Anruf pad, zfill verschiebt alle ursprünglich vorhergehenden + oder - zum Anfang der Zeichenfolge.

Beachten Sie, dass es nicht erforderlich ist, dass die ursprüngliche Zeichenfolge tatsächlich numerisch ist:

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

Für Postleitzahlen als ganze Zahlen gespeichert:

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

Kurzer Zeitvergleich:

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]

Ich habe verschiedene Tests von verschiedenen Wiederholungen gemacht. Die Unterschiede sind nicht riesig, aber in allen Tests, die zfill Lösung war am schnellsten.

Wenn der sauberste Weg mit >= Python 3.6 f-Strings mit String Formatierung :

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

Ein weiterer Ansatz wäre, eine Liste Verständnis mit einer Bedingung für Längen Überprüfung zu verwenden. Nachfolgend finden Sie eine Demonstration:

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

Sie können auch wiederholen „0“, prepend es str(n) und die am weitesten rechts liegende Breite Scheibe zu erhalten. Schnell und schmutzig wenig Ausdruck.

def pad_left(n, width, pad="0"):
    return ((pad * width) + str(n))[-width:]
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top