Sprintf como funcionalidad en Python

131

Me gustaría crear un buffer de cadena para hacer mucho procesamiento, formatear y finalmente escribir el buffer en un archivo de texto usando una sprintffuncionalidad de estilo C en Python. Debido a las declaraciones condicionales, no puedo escribirlas directamente en el archivo.

por ejemplo, seudocódigo:

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

Entonces, en el archivo de salida tenemos este tipo de o / p:

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

Editar, para aclarar mi pregunta:
buf es un gran búfer que contiene todas estas cadenas que se han formateado con sprintf. Siguiendo sus ejemplos, bufsolo contendrá valores actuales, no valores anteriores. por ejemplo, lo primero bufque escribí A= something ,B= somethingmás tarde C= somethingse agregó en el mismo buf, pero en sus respuestas de Python bufcontiene solo el último valor, que no es lo que quiero, quiero buftener todos los printfmensajes que he hecho desde el principio, como en C.

primavera
fuente
1
Esa no es la forma en que funciona sprintf () en C. (Escribe el contenido al principio buf, no al final). Probablemente funcionaría mejor usar una matriz de cadenas, luego unirlas antes de escribir en el archivo.
yam655
@dividebyzero ¿No es esto trivial en Python ya que es un lenguaje de programación general? Por ejemplo, vea la solución de Michael J. Barber (publicada después de su comentario). def sprintf(buf, fmt, *args): ...
jdk1.0
@ jdk1.0 No sé a qué me refería, era un programador joven e ingenuo de Python ... Esta pregunta es realmente extraña porque esta cosa de reutilización de búfer no es tan simple, necesitarías incrementar un puntero con la salida de cada llamada de sprintf, y este tipo de cosas no es algo de lo que deba preocuparse si está haciendo Python. De todos modos, ¡me alegro de haberme mudado a Scala y ahora a Julia!
dividebyzero

Respuestas:

169

Python tiene un %operador para esto.

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

Mira esto referencia para todos los especificadores de formato compatibles.

También podrías usar format:

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

Si entiendo su pregunta correctamente, format () es lo que está buscando, junto con su mini idioma .

Ejemplo tonto para python 2.7 y superior:

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

Para versiones anteriores de python: (probado con 2.6.2)

>>> print "{0} ...\r\n {1}!".format("Hello", "world")
Hello ...
 world!
Nicolas Lefebvre
fuente
44
Probablemente debería tener en cuenta que esa versión solo funciona en Python 3. En Python 2.6, por ejemplo, debe hacer:"{0} ...\r\n {1}!".format("Hello", "world")
Mark Longair
1
Editando mi respuesta para incluir eso; ¡aunque no funciona también para Python 2.7!
Nicolas Lefebvre
20

No estoy completamente seguro de entender su objetivo, pero puede usar una StringIOinstancia como un búfer:

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

A diferencia sprintf, simplemente pasa una cadena a buf.write, formateándola con el %operador o el formatmétodo de cadenas.

Por supuesto, podría definir una función para obtener la sprintfinterfaz que espera:

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

que se usaría así:

>>> 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
fuente
2
+1 por mostrarme cómo usar * args con el operador de formato de cadena (%).
Curtis Yallop
para usar Python3 en su io.StringIO()lugar
Wolf
11

Puede usar el formato de cadena:

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

Pero esto se elimina en Python 3, debe usar "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
fuente
44
Incorrecto, no se elimina en Python 3. Python 3.0 dijo que sería obsoleto en 3.1, pero creo que eso nunca sucedió. El uso format()puede ser preferible, pero %aún existe el formato. (Consulte mail.python.org/pipermail/python-dev/2009-September/092399.html para ver algunos de los motivos por los que no se desaprobó)
Duncan
1
@Duncan; Gracias, no lo sabía. Leí en alguna parte que se desaprobó y nunca lo intenté de nuevo :).
utdemir
7

Para insertar en una cadena muy larga, es bueno usar nombres para los diferentes argumentos, en lugar de esperar que estén en las posiciones correctas. Esto también facilita la sustitución de múltiples recurrencias.

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

Tomado de ejemplos de formato , donde Formattambién se muestran todas las otras respuestas relacionadas.

Jost
fuente
3

Esta es probablemente la traducción más cercana de su código C al código 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()

El %operador en Python hace casi exactamente lo mismo que C's sprintf. También puede imprimir la cadena en un archivo directamente. Si hay muchos de estos stringlets con formato de cadena involucrados, podría ser conveniente usar unStringIO objeto para acelerar el tiempo de procesamiento.

Entonces, en lugar de hacerlo +=, haz esto:

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

Si desea algo como la función de impresión python3 pero a una cadena:

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 sin el '\n'al final:

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}"
Miguel
fuente
1

Algo como...

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

Hello John
bmatovu
fuente
Esto imprime una cadena en la consola, no en una cadena, que es lo que quería el OP.
Teemu Leisti
Esto también imprimió% s en la pantalla, lo que no se esperaba; pero me gusta que puedo agregar múltiples variables con una coma.
b01
0

Dos enfoques son escribir en un buffer de cadena o escribir líneas en una lista y unirlas más tarde. Pienso que elStringIO enfoque es más pitónico, pero no funcionó antes de 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())

También puede usarlos sin un ContextMananger( s = StringIO()). Actualmente, estoy usando una clase de administrador de contexto con una printfunción. Este fragmento puede ser útil para poder insertar requisitos de depuración o paginación extraños:

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
fuente