Sprintf jak funkcjonalność w Pythonie

134

Chciałbym stworzyć bufor ciągów do wielu operacji przetwarzania, formatowania i na koniec zapisywania bufora w pliku tekstowym przy użyciu sprintffunkcji w stylu C w Pythonie. Z powodu instrukcji warunkowych nie mogę zapisać ich bezpośrednio do pliku.

np. pseudokod:

sprintf(buf,"A = %d\n , B= %s\n",A,B)
/* some processing */
sprint(buf,"C=%d\n",c)
....
...
fprintf(file,buf)

Więc w pliku wyjściowym mamy tego rodzaju o / p:

A= foo B= bar
C= ded
etc...

Edytuj, żeby wyjaśnić moje pytanie:
buf czy duży bufor zawiera wszystkie te ciągi, które zostały sformatowane przy użyciu sprintf. Idąc za swoimi przykładami, bufbędą zawierały tylko aktualne wartości, a nie starsze. np. pierwszy w, bufktóry napisałem A= something ,B= somethingpóźniej, C= somethingbył dodawany w tym samym buf, ale w twoim Pythonie odpowiedzi bufzawierają tylko ostatnią wartość, której nie chcę - chcę bufmieć wszystkie printfs, które zrobiłem od początku, jak w C.

wiosna
źródło
1
To nie jest sposób, w jaki sprintf () działa w C. (zapisuje zawartość na początku buf, a nie na końcu). Prawdopodobnie najlepiej byłoby użyć tablicy ciągów, a następnie połączyć je ze sobą przed zapisaniem do pliku.
yam655
@dividebyzero Czy to nie jest trywialne w Pythonie, ponieważ jest to ogólny język programowania? Na przykład zobacz rozwiązanie Michaela J. Barbera (opublikowane po Twoim komentarzu). def sprintf(buf, fmt, *args): ...
jdk1.0
@ jdk1.0 Nie wiem, co miałem na myśli, byłem młodym i naiwnym programistą Pythona ... To pytanie jest właściwie dziwne, ponieważ to ponowne użycie bufora nie jest takie proste, musiałbyś zwiększyć wskaźnik z wyjściem każde wywołanie sprintf, a tego rodzaju rzeczy nie są czymś, o co powinieneś się martwić, jeśli używasz Pythona. W każdym razie cieszę się, że przeniosłem się do Scali i teraz Julia!
dividebyzero

Odpowiedzi:

171

Python ma %do tego operator.

>>> a = 5
>>> b = "hello"
>>> buf = "A = %d\n , B = %s\n" % (a, b)
>>> print buf
A = 5
 , B = hello

>>> c = 10
>>> buf = "C = %d\n" % c
>>> print buf
C = 10

Zobacz tę dokumentację dla wszystkich obsługiwanych specyfikatorów formatu.

Równie dobrze możesz użyć format:

>>> print "This is the {}th tome of {}".format(5, "knowledge")
This is the 5th tome of knowledge
Alexei Sholik
źródło
41

Jeśli dobrze rozumiem twoje pytanie, format () jest tym, czego szukasz, wraz z jego mini językiem .

Głupi przykład dla Pythona 2.7 i nowszych:

>>> print "{} ...\r\n {}!".format("Hello", "world")
Hello ...
 world!

W przypadku wcześniejszych wersji Pythona: (testowane z 2.6.2)

>>> print "{0} ...\r\n {1}!".format("Hello", "world")
Hello ...
 world!
Nicolas Lefebvre
źródło
4
Prawdopodobnie powinieneś zauważyć, że ta wersja działa tylko w Pythonie 3. Na przykład w Pythonie 2.6 musisz zrobić:"{0} ...\r\n {1}!".format("Hello", "world")
Mark Longair
1
Edycja mojej odpowiedzi, aby to uwzględnić; nie myśl jednak, że działa również dla Pythona 2.7!
Nicolas Lefebvre,
21

Nie jestem do końca pewien, czy rozumiem Twój cel, ale możesz użyć StringIOinstancji jako bufora:

>>> import StringIO 
>>> buf = StringIO.StringIO()
>>> buf.write("A = %d, B = %s\n" % (3, "bar"))
>>> buf.write("C=%d\n" % 5)
>>> print(buf.getvalue())
A = 3, B = bar
C=5

W przeciwieństwie do tego sprintf, po prostu przekazujesz ciąg buf.write, formatując go za pomocą %operatora lub formatmetody łańcuchów.

Możesz oczywiście zdefiniować funkcję, aby uzyskać sprintfinterfejs, na który masz nadzieję:

def sprintf(buf, fmt, *args):
    buf.write(fmt % args)

który zostałby użyty w ten sposób:

>>> buf = StringIO.StringIO()
>>> sprintf(buf, "A = %d, B = %s\n", 3, "foo")
>>> sprintf(buf, "C = %d\n", 5)
>>> print(buf.getvalue())
A = 3, B = foo
C = 5
Michael J. Barber
źródło
2
+1 za pokazanie mi, jak używać * args z operatorem formatowania łańcucha (%).
Curtis Yallop
io.StringIO()zamiast tego użyj Pythona3
Wolf
11

Możesz użyć formatowania ciągów:

>>> a=42
>>> b="bar"
>>> "The number is %d and the word is %s" % (a,b)
'The number is 42 and the word is bar'

Ale to zostało usunięte w Pythonie 3, powinieneś użyć "str.format ()":

>>> a=42
>>> b="bar"
>>> "The number is {0} and the word is {1}".format(a,b)
'The number is 42 and the word is bar'
utdemir
źródło
5
Źle, nie jest usuwany w Pythonie 3. Python 3.0 powiedział, że będzie przestarzały w 3.1, ale uważam, że nigdy się to nie zdarzyło. Używanie format()może być lepsze, ale %formatowanie nadal istnieje. (Zobacz mail.python.org/pipermail/python-dev/2009-September/092399.html, aby zapoznać się z niektórymi powodami, dla których nie został wycofany)
Duncan
1
@Duncan; dzięki, nie wiedziałem tego. Czytałem gdzieś, że jest przestarzały i nigdy więcej nie próbowałem :).
utdemir
7

Aby wstawić do bardzo długiego ciągu, dobrze jest użyć nazw dla różnych argumentów, zamiast mieć nadzieję, że znajdują się one we właściwych miejscach. Ułatwia to również zastąpienie wielu powtórzeń.

>>> 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N', longitude='-115.81W')
'Coordinates: 37.24N, -115.81W'

Zaczerpnięte z przykładów formatu , w których Formatpokazane są również wszystkie inne powiązane odpowiedzi.

Jost
źródło
5

Jeśli chcesz czegoś takiego jak funkcja print python3, ale do łańcucha:

def sprint(*args, **kwargs):
    sio = io.StringIO()
    print(*args, **kwargs, file=sio)
    return sio.getvalue()
>>> x = sprint('abc', 10, ['one', 'two'], {'a': 1, 'b': 2}, {1, 2, 3})
>>> x
"abc 10 ['one', 'two'] {'a': 1, 'b': 2} {1, 2, 3}\n"

lub bez '\n'na końcu:

def sprint(*args, end='', **kwargs):
    sio = io.StringIO()
    print(*args, **kwargs, end=end, file=sio)
    return sio.getvalue()
>>> x = sprint('abc', 10, ['one', 'two'], {'a': 1, 'b': 2}, {1, 2, 3})
>>> x
"abc 10 ['one', 'two'] {'a': 1, 'b': 2} {1, 2, 3}"
Michał
źródło
3

Jest to prawdopodobnie najbliższe tłumaczenie twojego kodu C na kod Pythona.

A = 1
B = "hello"
buf = "A = %d\n , B= %s\n" % (A, B)

c = 2
buf += "C=%d\n" % c

f = open('output.txt', 'w')
print >> f, c
f.close()

%Operator w Pythonie robi prawie dokładnie tak samo jak C użytkownika sprintf. Możesz również wydrukować ciąg bezpośrednio do pliku. Jeśli zaangażowanych jest wiele stringletów sformatowanych jako ciągi znaków, rozsądne może być użycie StringIOobiektu w celu przyspieszenia czasu przetwarzania.

Więc zamiast robić +=, zrób to:

import cStringIO
buf = cStringIO.StringIO()

...

print >> buf, "A = %d\n , B= %s\n" % (A, B)

...

print >> buf, "C=%d\n" % c

...

print >> f, buf.getvalue()
YH Wong
źródło
1

Coś jak...

greetings = 'Hello {name}'.format(name = 'John')

Hello John
bmatovu
źródło
Spowoduje to również wydrukowanie% s na ekranie, czego nie oczekiwano; ale podoba mi się to, że mogę dodać wiele zmiennych przecinkiem.
b01
0

Dwa podejścia to zapisywanie w buforze łańcuchów lub zapisywanie wierszy do listy i dołączanie do nich później. Myślę, że StringIOpodejście jest bardziej pythonowe, ale nie działało przed wersją Python 2.6.

from io import StringIO

with StringIO() as s:
   print("Hello", file=s)
   print("Goodbye", file=s)
   # And later...
   with open('myfile', 'w') as f:
       f.write(s.getvalue())

Możesz ich również używać bez ContextMananger( s = StringIO()). Obecnie używam klasy menedżera kontekstu z printfunkcją. Ten fragment może być przydatny, aby móc wstawić debugowanie lub dziwne wymagania dotyczące stronicowania:

class Report:
    ... usual init/enter/exit
    def print(self, *args, **kwargs):
        with StringIO() as s:
            print(*args, **kwargs, file=s)
            out = s.getvalue()
        ... stuff with out

with Report() as r:
   r.print(f"This is {datetime.date.today()}!", 'Yikes!', end=':')
Charles Merriam
źródło