Sprintf come funzionalità in Python


131

Vorrei creare un buffer di stringhe per eseguire molte elaborazioni, formattare e infine scrivere il buffer in un file di testo utilizzando una sprintffunzionalità di tipo C in Python. A causa delle istruzioni condizionali, non riesco a scriverle direttamente nel file.

ad esempio pseudo codice:

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

Quindi nel file di output abbiamo questo tipo di o / p:

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

Modifica, per chiarire la mia domanda:
buf è un grosso buffer che contiene tutte queste stringhe che sono state formattate usando sprintf. Seguendo i tuoi esempi, bufconterrà solo i valori correnti, non quelli precedenti. ad esempio, il primo in cui bufho scritto in A= something ,B= somethingseguito è C= somethingstato aggiunto nello stesso buf, ma nelle risposte di Python bufcontiene solo l'ultimo valore, che non desidero: voglio bufavere tutti printfi messaggi che ho fatto dall'inizio, come in C.


1
Questo non è il modo in cui sprintf () funziona in C. (Scrive il contenuto all'inizio di buf, non alla fine.) Probabilmente funzionerebbe meglio per usare una matrice di stringhe, quindi unirle prima di scrivere nel file.
yam655,

@dividebyzero Non è banale in Python in quanto è un linguaggio di programmazione generale? Ad esempio, vedi la soluzione di Michael J. Barber (pubblicata dopo il tuo commento). def sprintf(buf, fmt, *args): ...
jdk1.0,

@ jdk1.0 Non so cosa volevo dire, ero un programmatore Python giovane e ingenuo ... Questa domanda è in realtà strana perché questa cosa di riutilizzo del buffer non è così semplice, avresti bisogno di incrementare un puntatore con l'output di ogni sprintf call e questo genere di cose non è qualcosa di cui dovresti preoccuparti se stai facendo Python. Ad ogni modo, sono contento di essere passato alla Scala e ora a Julia!
dividebyzero,

Risposte:


170

Python ha un %operatore per questo.

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

Vedi questo riferimento per tutti gli identificatori di formato supportati.

Puoi anche usare format:

>>> print "This is the {}th tome of {}".format(5, "knowledge")
This is the 5th tome of knowledge

40

Se capisco correttamente la tua domanda, format () è quello che stai cercando, insieme al suo mini-linguaggio .

Esempio sciocco per Python 2.7 e versioni successive:

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

Per le versioni precedenti di Python: (testato con 2.6.2)

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

4
Probabilmente dovresti notare che quella versione funziona solo in Python 3. In Python 2.6, ad esempio, devi fare:"{0} ...\r\n {1}!".format("Hello", "world")
Mark Longair

1
Modifica la mia risposta per includerla; non funziona però anche con Python 2.7!
Nicolas Lefebvre,

20

Non sono del tutto sicuro di aver compreso il tuo obiettivo, ma puoi utilizzare StringIOun'istanza come buffer:

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

Diversamente sprintf, basta passare una stringa a buf.write, formattandola con l' %operatore o il formatmetodo delle stringhe.

Ovviamente potresti definire una funzione per ottenere l' sprintfinterfaccia che speri:

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

che sarebbe usato in questo modo:

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

2
+1 per avermi mostrato come usare * args con l'operatore di formattazione delle stringhe (%).
Curtis Yallop,

per Python3 utilizzare io.StringIO()invece
Wolf


11

Puoi usare la formattazione delle stringhe:

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

Ma questo viene rimosso in Python 3, dovresti usare "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'

4
Sbagliato, non è stato rimosso in Python 3. Python 3.0 ha detto che sarebbe stato deprecato in 3.1 ma credo che non sia mai successo. L'uso format()può essere preferibile ma la %formattazione esiste ancora. (Vedi mail.python.org/pipermail/python-dev/2009-September/092399.html per alcuni dei motivi per cui non è stato deprecato)
Duncan

1
@Duncan; grazie, non lo sapevo. Ho letto da qualche parte che è deprecato e non ho mai più provato :).
utdemir,

7

Per inserire in una stringa molto lunga è bello usare i nomi per i diversi argomenti, invece di sperare che siano nelle giuste posizioni. Ciò semplifica anche la sostituzione di più ricorrenze.

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

Tratto da esempi di formato , in cui Formatsono mostrate anche tutte le risposte correlate agli altri .


3

Questa è probabilmente la traduzione più vicina dal tuo codice C al codice Python.

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

L' %operatore in Python fa quasi esattamente la stessa cosa di C di sprintf. È inoltre possibile stampare direttamente la stringa su un file. Se sono coinvolti molti di questi stringlet in formato stringa, potrebbe essere saggio utilizzare un StringIOoggetto per accelerare i tempi di elaborazione.

Quindi, invece di farlo +=, fai questo:

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

3

Se vuoi qualcosa di simile alla funzione di stampa python3 ma a una stringa:

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"

o senza '\n'alla fine:

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

1

Qualcosa di simile a...

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

Hello John

Questo stampa una stringa sulla console, non su una stringa, che è ciò che voleva l'OP.
Teemu Leisti,

Questo ha anche stampato% s sullo schermo, cosa non prevista; ma mi piace poter aggiungere più variabili con una virgola.
b01


0

Due approcci sono scrivere in un buffer di stringhe o scrivere righe in un elenco e unirle successivamente. Penso che l' StringIOapproccio sia più pitonico, ma non ha funzionato prima di 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())

Puoi anche usarli senza un ContextMananger( s = StringIO()). Attualmente sto usando una classe di gestore di contesto con una printfunzione. Questo frammento potrebbe essere utile per poter inserire i requisiti di debug o paging dispari:

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=':')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.