csv.QUOTE_STRINGS é uma constante do módulo csv criada para controlar a forma como valores são escritos e lidos em arquivos CSV. Ela orienta o escritor a colocar aspas em campos de texto, enquanto valores não textuais podem ser gravados sem aspas. Na leitura, campos sem aspas podem ser convertidos de maneira semelhante ao modo QUOTE_NONNUMERIC, enquanto campos entre aspas permanecem como strings. Esse comportamento é útil quando você deseja preservar a diferença entre texto e número sem criar uma camada manual de serialização.
Neste guia, você vai entender como usar csv.QUOTE_STRINGS, quais problemas ele resolve, como ele se compara a outros modos de cotação e quais cuidados adotar em integrações reais.
O problema que o QUOTE_STRINGS resolve
CSV parece simples, mas não possui um sistema de tipos universal. O conteúdo 42 pode representar um número, um código, uma matrícula ou um identificador que não deve sofrer conversão. Ao mesmo tempo, alguns consumidores esperam que textos estejam entre aspas e números não.
import csv
linhas = [
["produto", "quantidade", "preco"],
["Curso Python", 2, 149.9],
]
with open("vendas.csv", "w", newline="", encoding="utf-8") as arquivo:
writer = csv.writer(arquivo, quoting=csv.QUOTE_STRINGS)
writer.writerows(linhas)
O resultado tende a manter strings cotadas e números sem aspas. Isso torna o arquivo mais expressivo para sistemas que interpretam a presença de aspas como um sinal de texto.
Comparação com outros modos
O módulo csv oferece modos como QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONE e QUOTE_NONNUMERIC. Cada um atende a um contrato diferente.
QUOTE_MINIMAL adiciona aspas apenas quando necessárias por causa de delimitadores, quebras de linha ou caracteres especiais. QUOTE_ALL coloca aspas em todos os campos. QUOTE_NONE evita aspas e exige atenção ao caractere de escape. QUOTE_NONNUMERIC coloca aspas em valores não numéricos e tenta converter campos não cotados para float durante a leitura.
QUOTE_STRINGS é mais explícito: sua intenção principal é cotar strings. Ele combina bem com modelos nos quais texto e número têm significados distintos e você deseja que essa diferença apareça no arquivo.
Escrita com DictWriter
O modo também funciona com DictWriter, muito usado em exportações de APIs e bancos de dados.
import csv
registros = [
{"nome": "Ana", "idade": 29, "saldo": 1250.50},
{"nome": "Bruno", "idade": 34, "saldo": 980.00},
]
with open("clientes.csv", "w", newline="", encoding="utf-8") as arquivo:
writer = csv.DictWriter(
arquivo,
fieldnames=["nome", "idade", "saldo"],
quoting=csv.QUOTE_STRINGS,
)
writer.writeheader()
writer.writerows(registros)
Esse padrão é interessante para relatórios financeiros, catálogos, exportações administrativas e arquivos trocados entre serviços.
Leitura e conversão de tipos
Na leitura, é importante conhecer o contrato do modo escolhido. Campos sem aspas podem ser interpretados numericamente, enquanto campos cotados permanecem strings. Isso significa que o arquivo precisa ter sido produzido de maneira consistente.
import csv
with open("clientes.csv", newline="", encoding="utf-8") as arquivo:
reader = csv.reader(arquivo, quoting=csv.QUOTE_STRINGS)
for linha in reader:
print(linha)
Não use a conversão implícita como única validação. Verifique quantidade de colunas, valores obrigatórios, limites e regras de domínio. Um número válido sintaticamente ainda pode estar fora do intervalo aceito.
Strings que parecem números
Um dos casos mais importantes envolve textos como CEP, código de produto, telefone, CPF mascarado, número de pedido ou identificador com zeros à esquerda.
dados = [
["codigo", "quantidade"],
["000127", 4],
]
Como "000127" é uma string, ele deve permanecer cotado. Assim, leitores que respeitam o contrato não o transformam em 127. A preservação de zeros à esquerda é uma razão prática para escolher esse modo.
Valores None e campos vazios
Campos nulos exigem uma decisão de negócio. Dependendo da versão do Python e da constante usada, None pode ser representado como campo vazio não cotado, permitindo distingui-lo de uma string vazia cotada. Antes de depender desse comportamento, escreva testes com a versão mínima do projeto.
linha = [None, "", "texto", 0]
Essa distinção é útil, mas deve ser documentada. Sistemas externos podem não diferenciar campo vazio, string vazia e valor ausente.
Delimitadores e dialetos
QUOTE_STRINGS pode ser combinado com delimitadores diferentes, como ponto e vírgula, comum em planilhas configuradas para localidades que usam vírgula decimal.
with open("dados.csv", "w", newline="", encoding="utf-8") as arquivo:
writer = csv.writer(
arquivo,
delimiter=";",
quoting=csv.QUOTE_STRINGS,
lineterminator="\n",
)
writer.writerow(["item", "valor"])
writer.writerow(["Assinatura", 99.9])
Defina explicitamente delimitador, codificação e terminador de linha quando o arquivo for consumido por outro sistema. Isso reduz diferenças entre Windows, Linux, Excel e ferramentas de BI.
Validação antes de exportar
Uma exportação confiável deve normalizar os dados antes de chamar o writer. Datas, decimais, enums e objetos personalizados não possuem um formato CSV universal.
from decimal import Decimal
from datetime import date
def serializar(valor):
if isinstance(valor, Decimal):
return format(valor, "f")
if isinstance(valor, date):
return valor.isoformat()
return valor
Perceba que converter um decimal para string faz com que ele seja cotado. Isso pode ser desejado para preservar precisão textual, mas talvez não seja o contrato esperado pelo consumidor. Decida conscientemente.
Integração com pandas e planilhas
Bibliotecas como pandas possuem suas próprias opções de cotação e inferência de tipos. Ao alternar entre csv e pandas, valide se os modos são equivalentes. Planilhas também podem reformatar datas, remover zeros à esquerda ou usar notação científica.
Para fluxos baseados em arquivos, o artigo sobre pathlib.Path.walk no Python ajuda a percorrer diretórios, enquanto mimetypes.guess_file_type no Python pode apoiar validações de tipo. Em pipelines concorrentes, veja queue.SimpleQueue no Python. Para configurações tipadas, consulte dataclasses.KW_ONLY no Python.
Testes recomendados
Crie testes de ida e volta: escreva os dados, leia novamente e compare valores e tipos. Inclua vírgulas, aspas, quebras de linha, Unicode, None, strings vazias, números negativos, notação científica e identificadores com zeros à esquerda.
import csv
import io
def roundtrip(linha):
buffer = io.StringIO(newline="")
writer = csv.writer(buffer, quoting=csv.QUOTE_STRINGS)
writer.writerow(linha)
buffer.seek(0)
return next(csv.reader(buffer, quoting=csv.QUOTE_STRINGS))
Testes assim revelam rapidamente se o contrato real coincide com suas expectativas.
Compatibilidade de versão
csv.QUOTE_STRINGS é um recurso recente. Confirme a versão mínima do Python no ambiente de produção e no CI. Quando precisar oferecer suporte a versões antigas, crie uma estratégia alternativa, como pré-processar os campos ou usar outro modo de cotação.
A documentação oficial do módulo csv é a referência principal. Para o formato e suas limitações, consulte também a especificação RFC 4180.
Boas práticas
Documente o delimitador, a codificação, a regra de nulos e a política de tipos. Não presuma que todos os leitores interpretam aspas da mesma forma. Mantenha exemplos de arquivos válidos, valide entradas e registre erros com número da linha.
Ao receber CSV de fontes não confiáveis, limite o tamanho, o número de colunas e o comprimento dos campos. Também avalie risco de CSV injection quando o conteúdo será aberto em planilhas, principalmente para strings iniciadas por =, +, - ou @.
Conclusão
csv.QUOTE_STRINGS oferece um contrato claro para cotar textos e deixar valores não textuais sem aspas. Ele ajuda a preservar identificadores, diferenciar strings de números e tornar exportações mais previsíveis.
Use o recurso quando o consumidor compreender esse contrato, escreva testes de ida e volta e trate explicitamente valores especiais. CSV continua sendo um formato textual simples; a confiabilidade depende de regras bem documentadas entre produtor e consumidor.







