7. Entrada e Saída
******************

Existem várias maneiras de apresentar a saída de um programa; os dados
podem ser exibidos ou impressos em forma legível para seres humanos,
ou escritos em arquivos para uso posterior. Este capítulo apresentará
algumas das possibilidades.


7.1. Refinando a formatação de saída
====================================

Até agora vimos duas maneiras de exibir valores: *expressões* e a
função "print()". (Uma outra maneira é utilizar o método "write()" de
objetos arquivo; o arquivo saída padrão pode ser referenciada como
"sys.stdout". Veja a Referência da Biblioteca Python para mais
informações sobre isto.)

Frequentemente é desejável mais controle sobre a formatação de saída
do que simplesmente exibir valores separados por espaços. Existem duas
formas de formatar a saída. A primeira é manipular strings através de
fatiamento (slicing) e concatenação. Os tipos string têm métodos úteis
para criar strings com tamanhos determinados, usando caracteres de
preenchimento; eles serão apresentados a seguir. A segunda forma é
usar o método "str.format()".

O módulo "string" tem uma classe "string.Template" que oferece uma
outra maneira de inserir valores em strings.

Permanece a questão: como converter valores para strings? Felizmente,
Python possui duas maneiras de converter qualquer valor para uma
string: as funções "repr()" e "str()".

A função "str()" serve para retornar representações de valores que
sejam legíveis para as pessoas, enquanto "repr()" é para gerar
representações que o interpretador Python consegue ler (ou levantará
uma exceção "SyntaxError", se não houver sintaxe equivalente). Para
objetos que não têm uma representação adequada para consumo humano,
"str()" devolve o mesmo valor que "repr()". Muitos valores, tal como
números ou estruturas, como listas e dicionários, têm a mesma
representação usando quaisquer das funções. Strings, em particular,
têm duas representações distintas.

Alguns exemplos:

   >>> s = 'Hello, world.'
   >>> str(s)
   'Hello, world.'
   >>> repr(s)
   "'Hello, world.'"
   >>> str(1/7)
   '0.14285714285714285'
   >>> x = 10 * 3.25
   >>> y = 200 * 200
   >>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
   >>> print(s)
   The value of x is 32.5, and y is 40000...
   >>> # The repr() of a string adds string quotes and backslashes:
   ... hello = 'hello, world\n'
   >>> hellos = repr(hello)
   >>> print(hellos)
   'hello, world\n'
   >>> # The argument to repr() may be any Python object:
   ... repr((x, y, ('spam', 'eggs')))
   "(32.5, 40000, ('spam', 'eggs'))"

A seguir, duas maneiras de se escrever uma tabela de quadrados e
cubos::

   >>> for x in range(1, 11):
   ...     print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
   ...     # Note use of 'end' on previous line
   ...     print(repr(x*x*x).rjust(4))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

   >>> for x in range(1, 11):
   ...     print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

(Note that in the first example, one space between each column was
added by the way "print()" works: by default it adds spaces between
its arguments.)

Esse exemplo demonstra o método "str.rjust()" de objetos string, que
alinha uma string à direita juntando espaços adicionais à esquerda.
Existem métodos análogas "str.ljust()" e "str.center()". Esses métodos
não exibem nada na tela, apenas devolvem uma nova string formatada. Se
a entrada extrapolar o comprimento especificado, a string original é
devolvida sem modificação; isso pode estragar o alinhamento das
colunas, mas é melhor do que a alternativa, que seria apresentar um
valor mentiroso. (Se for realmente desejável truncar o valor, pode-se
usar fatiamento, por exemplo: "x.ljust(n)[:n]".)

Existe ainda o método "str.zfill()" que preenche uma string numérica
com zeros à esquerda, e  sabe lidar com sinais positivos e negativos:

   >>> '12'.zfill(5)
   '00012'
   >>> '-3.14'.zfill(7)
   '-003.14'
   >>> '3.14159265359'.zfill(5)
   '3.14159265359'

Um uso básico do método "str.format()" tem esta forma:

   >>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
   We are the knights who say "Ni!"

As chaves e seus conteúdos (chamados campos de formatação) são
substituídos pelos objetos passados para o método "str.format()".  Um
número nas chaves pode ser usado para referenciar a posição do objeto
passado no método "str.format()".

   >>> print('{0} and {1}'.format('spam', 'eggs'))
   spam and eggs
   >>> print('{1} and {0}'.format('spam', 'eggs'))
   eggs and spam

Se argumentos nomeados são passados para o método "str.format()", seus
valores serão referenciados usando o nome do argumento:

   >>> print('This {food} is {adjective}.'.format(
   ...       food='spam', adjective='absolutely horrible'))
   This spam is absolutely horrible.

Argumentos posicionais e nomeados podem ser combinados à vontade:

   >>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
                                                          other='Georg'))
   The story of Bill, Manfred, and Georg.

"'!a'" (apply "ascii()"), "'!s'" (apply "str()") and "'!r'" (apply
"repr()") can be used to convert the value before it is formatted:

   >>> contents = 'eels'
   >>> print('My hovercraft is full of {}.'.format(contents))
   My hovercraft is full of eels.
   >>> print('My hovercraft is full of {!r}.'.format(contents))
   My hovercraft is full of 'eels'.

Após o identificador do campo, uma especificação de formato opcional
pode ser colocada depois de ":" (dois pontos). O exemplo abaixo
arredonda Pi até a terceira casa após o ponto decimal.

>>> import math
>>> print('The value of PI is approximately {0:.3f}.'.format(math.pi))
The value of PI is approximately 3.142.

Colocar um inteiro *n* logo após o ":" fará o campo ocupar uma largura
mínima de *n* caracteres. Isto é útil para organizar tabelas.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
   >>> for name, phone in table.items():
   ...     print('{0:10} ==> {1:10d}'.format(name, phone))
   ...
   Jack       ==>       4098
   Dcab       ==>       7678
   Sjoerd     ==>       4127

Se uma string de formatação é muito longa, e não se deseja quebrá-la,
pode ser bom referir-se aos valores a serem formatados por nome, em
vez de posição. Isto pode ser feito passando um dicionário usando
colchetes "[]" para acessar as chaves:

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
   ...       'Dcab: {0[Dcab]:d}'.format(table))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Isto também pode ser feito passando o dicionário como argumento do
método, usando a notação "**":

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Isto é particularmente útil em conjunto com a função embutida
"vars()", que devolve um dicionário contendo todas as variáveis
locais.

Para uma visão completa da formatação de strings com "str.format()",
veja a seção Sintaxe das strings de formato.


7.1.1. Formatação de strings à moda antiga
------------------------------------------

O operador "%" também pode ser usado para formatação de strings. O
operando da esquerda é interpretado de forma semelhante ao estilo de
formatação da função "sprintf()" da linguagem C, aplicando a
formatação ao operando da direita, e devolvendo a string resultante.
Por exemplo:

   >>> import math
   >>> print('The value of PI is approximately %5.3f.' % math.pi)
   The value of PI is approximately 3.142.

Mais informação pode ser encontrada na seção Formatação de String no
Formato printf-style.


7.2. Leitura e escrita de arquivos
==================================

A função "open()" devolve um *file object*, e é frequentemente usada
com dois argumentos: "open(filename, mode)".

   >>> f = open('workfile', 'w')

O primeiro argumento é uma string contendo o nome do arquivo. O
segundo argumento é outra string, contendo alguns caracteres que
descrevem o modo como o arquivo será usado. O parâmetro *mode* pode
ser "'r'" quando o arquivo será apenas lido, "'w'" para escrever (se o
arquivo já existir seu conteúdo prévio será apagado), e "'a'" para
abrir o arquivo para adição; qualquer escrita será adicionada ao final
do arquivo. A opção "'r+'" abre o arquivo tanto para leitura como para
escrita. O parâmetro "mode" é opcional, em caso de omissão será
assumido "'r'".

Normalmente, os arquivos são abertos em *modo texto*, ou seja, você lê
e grava strings de e para o arquivo, numa codificação específica. Se a
codificação não for especificada, o padrão é dependente da plataforma
(consulte "open()"). "'b'" anexado ao modo abre o arquivo em *modo
binário*: agora os dados são lidos e escritos na forma de bytes. Este
modo deve ser usado para todos os arquivos que não contenham texto.

Em modo texto, o padrão durante a leitura é converter terminadores de
linha específicos da plataforma ("\n" no Unix, "\r\n" no Windows) para
apenas "\n". Ao escrever no modo de texto, o padrão é converter as
ocorrências de "\n" de volta para os finais de linha específicos da
plataforma. Essa modificação de bastidores nos dados do arquivo é
adequada para arquivos de texto, mas corromperá dados binários, como
arquivos "JPEG" ou "EXE". Tenha muito cuidado para só usar o modo
binário, ao ler e gravar esses arquivos.

It is good practice to use the "with" keyword when dealing with file
objects.  The advantage is that the file is properly closed after its
suite finishes, even if an exception is raised at some point.  Using
"with" is also much shorter than writing equivalent "try"-"finally"
blocks:

   >>> with open('workfile') as f:
   ...     read_data = f.read()
   >>> f.closed
   True

Se você não estiver usando a palavra-chave "with", então você deve
chamar "f.close()" para fechar o arquivo e liberar imediatamente
quaisquer recursos do sistema usados por ele. Se você não fechar
explicitamente um arquivo, o coletor de lixo do Python irá
eventualmente destruir o objeto e fechar o arquivo aberto para você,
mas o arquivo pode ficar aberto por um tempo. Outro risco é que
diferentes implementações de Python farão essa limpeza em momentos
diferentes.

Depois que um arquivo é fechado, seja por uma instrução "with" ou
chamando "f.close()", as tentativas de usar o arquivo falharão
automaticamente.

   >>> f.close()
   >>> f.read()
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   ValueError: I/O operation on closed file.


7.2.1. Métodos de arquivos
--------------------------

Para simplificar, o resto dos exemplos nesta seção assumem que um
objeto arquivo chamado "f" já foi criado.

Para ler o conteúdo de um arquivo, chame "f.read(size)", que lê uma
quantidade de dados e retorna-os como uma string (em modo texto) ou
bytes (em modo binário). O argumento numérico *size* é opcional.
Quando *size* é omitido ou negativo, todo o conteúdo do arquivo é lido
e retornado; se o arquivo é duas vezes maior que a memória da máquina,
o problema é seu. Caso contrário, no máximo *size* bytes serão lidos e
retornados. Se o fim do arquivo for atingido, "f.read()" retornará uma
string vazia (*""*).

   >>> f.read()
   'This is the entire file.\n'
   >>> f.read()
   ''

O método "f.readline()" lê uma única linha do arquivo; o caractere de
quebra de linha ("\n") é mantido ao final da string, e só é omitido na
última linha do arquivo, se o arquivo não termina com uma quebra de
linha. Isso elimina a ambiguidade do valor retornado; se
"f.readline()" devolver uma string vazia, o fim do arquivo foi
atingido. Linhas em branco são representadas por um "'\n'" -- uma
string contendo apenas o terminador de linha.

   >>> f.readline()
   'This is the first line of the file.\n'
   >>> f.readline()
   'Second line of the file\n'
   >>> f.readline()
   ''

Uma maneira alternativa de ler linhas do arquivo é iterar diretamente
pelo objeto arquivo. É eficiente, rápido e resulta em código mais
simples:

   >>> for line in f:
   ...     print(line, end='')
   ...
   This is the first line of the file.
   Second line of the file

Se desejar ler todas as linhas de um arquivo em uma lista pode-se usar
"list(f)" ou "f.readlines()".

"f.write(string)" escreve o conteúdo de *string* para o arquivo,
retornando o número de caracteres escritos.

   >>> f.write('This is a test\n')
   15

Outros tipos de objetos precisam ser convertidos -- seja para uma
string (em modo texto) ou para bytes (em modo binário) -- antes de
escrevê-los:

   >>> value = ('the answer', 42)
   >>> s = str(value)  # convert the tuple to string
   >>> f.write(s)
   18

"f.tell()" retorna um inteiro dando a posição atual do objeto de
arquivo, no arquivo representado, como número de bytes desde o início
do arquivo, no modo binário, e um número ininteligível, quando no modo
de texto.

Para mudar a posição do objeto arquivo, use "f.seek(offset,
from_what)".  A posição é calculada somando-se *offset* ao ponto de
referência; o ponto de referência é selecionado a partir do argumento
*from_what*.  Um valor 0 para *from_what* indica o início do arquivo,
1 usa a posição atual no arquivo, e 2 usa o fim do arquivo como ponto
de referência.  *from_what* pode ser omitido e assumirá o valor 0,
usando o início do arquivo como ponto de referência.

   >>> f = open('workfile', 'rb+')
   >>> f.write(b'0123456789abcdef')
   16
   >>> f.seek(5)      # Go to the 6th byte in the file
   5
   >>> f.read(1)
   b'5'
   >>> f.seek(-3, 2)  # Go to the 3rd byte before the end
   13
   >>> f.read(1)
   b'd'

Em arquivos texto (abertos sem um "b", em modo string), somente
*seeks* relativos ao início do arquivo serão permitidos (exceto se for
indicado o final do arquivo, com "seek(0, 2)") e o único valor válido
para *offset* são aqueles retornados por chamada à "f.tell()", ou
zero. Qualquer outro valor para *offset* produz um comportamento
indefinido.

Objetos arquivo tem alguns método adicionais, como "isatty()" e
"truncate()" que não são usados com frequência; consulte a Biblioteca
de Referência para um guia completo de objetos arquivo.


7.2.2. Gravando dados estruturados com "json"
---------------------------------------------

Strings podem ser facilmente gravadas e lidas em um arquivo. Números
dão um pouco mais de trabalho, já que o método "read()" só retorna
strings, que terão que ser passadas para uma função como "int()", que
pega uma string como "'123'" e retorna seu valor numérico 123.  Quando
você deseja salvar tipos de dados mais complexos, como listas e
dicionários aninhados, a análise e serialização manual tornam-se
complicadas.

Ao invés de ter usuários constantemente escrevendo e depurando código
para gravar tipos complicados de dados em arquivos, o Python permite
que se use o popular formato de troca de dados chamado JSON
(JavaScript Object Notation). O módulo padrão chamado "json" pode
pegar hierarquias de dados em Python e convertê-las em representações
de strings; esse processo é chamado *serialização*. Reconstruir os
dados da representação string é chamado *desserialização*. Entre
serializar e desserializar, a string que representa o objeto pode ser
armazenada em um arquivo ou estrutura de dados, ou enviada por uma
conexão de rede para alguma outra máquina.

Nota:

  O formato JSON é comumente usado por aplicativos modernos para
  permitir a troca de dados. Pessoas que programam já estão
  familiarizados com esse formato, o que o torna uma boa opção para
  interoperabilidade.

Um objeto "x", pode ser visualizado na sua representação JSON com uma
simples linha de código:

   >>> import json
   >>> json.dumps([1, 'simple', 'list'])
   '[1, "simple", "list"]'

Outra variação da função "dumps()", chamada "dump()", serializa o
objeto para um *arquivo texto*.  Se "f" é um *arquivo texto* aberto
para escrita, podemos fazer isto:

   json.dump(x, f)

Para decodificar o objeto novamente, se "f" é um *arquivo texto* que
foi aberto para leitura:

   x = json.load(f)

Essa técnica de serialização simples pode manipular listas e
dicionários, mas a serialização de instâncias de classe arbitrárias no
JSON requer um pouco mais de esforço. A referência para o módulo
"json" contém uma explicação disso.

Ver também:

  O módulo "pickle"

  Ao contrário do JSON, *pickle* é um protocolo que permite a
  serialização de objetos Python arbitrariamente complexos. Por isso,
  é específico do Python e não pode ser usado para se comunicar com
  aplicativos escritos em outros idiomas. Também é inseguro por
  padrão: desserializar dados de pickle, provenientes de uma fonte não
  confiável, pode executar código arbitrário, se os dados foram
  criados por um invasor habilidoso.
