Sprintf como funcionalidade em Python

131

Gostaria de criar um buffer de cadeia para processar, formatar e finalmente gravar o buffer em um arquivo de texto usando uma sprintffuncionalidade de estilo C em Python. Por causa de instruções condicionais, não posso gravá-las diretamente no arquivo.

por exemplo, pseudo código:

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

Portanto, no arquivo de saída, temos este tipo de o / p:

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

Edite, para esclarecer minha pergunta:
buf é um grande buffer que contém todas essas strings que foram formatadas usando o sprintf. Seguindo seus exemplos, bufconterá apenas valores atuais, não valores mais antigos. por exemplo, o primeiro em bufque escrevi A= something ,B= somethingmais tarde C= somethingfoi anexado no mesmo buf, mas em suas respostas em Python bufcontém apenas o último valor, o que não é o que eu quero - quero bufter todos os printfs que fiz desde o início, como em C.

Primavera
fonte
1
Não é assim que o sprintf () funciona em C. (Ele grava o conteúdo no início bufe não no final.) Provavelmente seria melhor usar uma matriz de seqüências de caracteres e juntá-las antes de gravar no arquivo.
Yam655
@dividebyzero Isso não é trivial no Python, pois é uma linguagem de programação geral? Por exemplo, consulte a solução de Michael J. Barber (publicada após o seu comentário). def sprintf(buf, fmt, *args): ...
jdk1.0
@ jdk1.0 Eu não sei o que eu quis dizer, eu era jovem e ingênuo programador Python ... Essa questão é realmente estranha porque essa coisa de reutilização de buffer não é tão simples assim, você precisa incrementar um ponteiro com a saída de cada sprintf chama, e esse tipo de coisa não é algo com que você deveria se preocupar se estiver usando Python. De qualquer forma, estou feliz por ter mudado para Scala e agora Julia!
dividebyzero

Respostas:

169

Python tem um %operador para isso.

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

Consulte esta referência para todos os especificadores de formato suportados.

Você também pode usar format:

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

Se eu entendi sua pergunta corretamente, format () é o que você está procurando, junto com seu mini-idioma .

Exemplo bobo para python 2.7 e superior:

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

Para versões anteriores do python: (testado com 2.6.2)

>>> print "{0} ...\r\n {1}!".format("Hello", "world")
Hello ...
 world!
Nicolas Lefebvre
fonte
4
Provavelmente, você deve observar que essa versão funciona apenas no Python 3. No Python 2.6, por exemplo, você precisa fazer:"{0} ...\r\n {1}!".format("Hello", "world")
Mark Longair 15/03/11
1
Editando minha resposta para incluir isso; não que ele também funcione para python 2.7!
Nicolas Lefebvre
20

Não tenho certeza absoluta de que entendi seu objetivo, mas você pode usar uma StringIOinstância como um 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

Ao contrário sprintf, você apenas passa uma string para buf.write, formatando-a com o %operador ou o formatmétodo de strings.

Obviamente, você pode definir uma função para obter a sprintfinterface que deseja:

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

que seria usado assim:

>>> 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
fonte
2
+1 por me mostrar como usar * args com o operador de formatação de string (%).
Curtis Yallop
para Python3 usar io.StringIO()vez
Lobo
13

Use o operador de formatação% :

buf = "A = %d\n , B= %s\n" % (a, b)
print >>f, buf
NPE
fonte
11

Você pode usar a formatação de string:

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

Mas isso foi removido no Python 3, você deve 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
fonte
4
Errado, ele não foi removido no Python 3. O Python 3.0 disse que seria reprovado no 3.1, mas acredito que isso nunca aconteceu. O uso format()pode ser preferível, mas a %formatação ainda existe. (Veja mail.python.org/pipermail/python-dev/2009-September/092399.html para algumas das razões por que não foi reprovado)
Duncan
1
@Duncan; obrigado, eu não sabia disso. Eu li em algum lugar preterido e nunca tentei novamente :).
utdemir
7

Para inserir em uma string muito longa, é bom usar nomes para os diferentes argumentos, em vez de esperar que eles estejam nas posições corretas. Isso também facilita a substituição de várias recorrências.

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

Retirado de exemplos de formato , onde todas as outras Formatrespostas relacionadas também são mostradas.

Jost
fonte
3

Esta é provavelmente a tradução mais próxima do seu código C para o 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()

O %operador em Python faz quase exatamente a mesma coisa que os C's sprintf. Você também pode imprimir a sequência em um arquivo diretamente. Se houver muitas dessas strings formatadas em sequência envolvidas, pode ser aconselhável usar um StringIOobjeto para acelerar o tempo de processamento.

Então, em vez de fazer +=, faça o seguinte:

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

Se você deseja algo como a função de impressão python3, mas para uma string:

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"

ou sem o '\n'no 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}"
Michael
fonte
1

Algo como...

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

Hello John
bmatovu
fonte
Isso imprime uma string no console, não em uma string, que é o que o OP queria.
Teemu Leisti 12/11/19
Isso também imprimiu% ​​s na tela, o que não era esperado; mas gosto que eu possa adicionar várias variáveis ​​com uma vírgula.
b01
0

Duas abordagens são gravar em um buffer de cadeia ou escrever linhas em uma lista e juntá-las mais tarde. Eu acho que a StringIOabordagem é mais pitônica, mas não funcionou antes do 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())

Você também pode usá-los sem um ContextMananger( s = StringIO()). Atualmente, estou usando uma classe de gerenciador de contexto com uma printfunção. Esse fragmento pode ser útil para poder inserir requisitos de depuração ou paginação ímpar:

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
fonte