csv.QUOTE_STRINGS: preserve tipos em arquivos CSV

Publicado em: 15/09/2026
Tempo de leitura: 6 minutos
Análise de dados CSV com csv.QUOTE_STRINGS no Python

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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código e caminhos de arquivos para PurePath.full_match no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    PurePath.full_match: valide caminhos com glob

    Aprenda PurePath.full_match no Python para validar caminhos completos com padrões glob, controlar maiúsculas e evitar filtros imprecisos.

    Ler mais

    Tempo de leitura: 6 minutos
    15/09/2026
    Código assíncrono representando asyncio.eager_task_factory no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    asyncio.eager_task_factory: reduza overhead de tarefas

    Aprenda asyncio.eager_task_factory no Python para reduzir overhead, entender mudanças de ordem e otimizar corrotinas curtas com segurança.

    Ler mais

    Tempo de leitura: 4 minutos
    14/09/2026
    Desenvolvedor trabalhando com timestamps UTC e calendar.timegm no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    calendar.timegm: converta UTC para timestamp Unix

    Aprenda calendar.timegm no Python para converter datas UTC em timestamps Unix com segurança, testes e integração com datetime.

    Ler mais

    Tempo de leitura: 6 minutos
    14/09/2026
    Programador analisando código para identificar tipos MIME de arquivos no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: detecte tipos MIME

    Aprenda mimetypes.guess_file_type no Python para detectar tipos MIME em caminhos, URLs, uploads e respostas HTTP com fallbacks seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Componentes de servidor representando interpretadores Python executando em paralelo
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: paralelismo real no Python

    Aprenda InterpreterPoolExecutor no Python para executar tarefas CPU-bound em interpretadores isolados com paralelismo real.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocessador representando CPUs disponíveis para um processo Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: conte CPUs disponíveis

    Aprenda os.process_cpu_count no Python para dimensionar workers conforme as CPUs realmente disponíveis ao processo.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026