quopri: codifique e decodifique quoted-printable

Atualizado em: 20/08/2026
Tempo de leitura: 7 minutos
Mensagem digital representando codificação quoted-printable com quopri no Python

Mensagens de e-mail e protocolos antigos precisam transportar texto por canais que nem sempre preservam todos os bytes. O formato quoted-printable resolve esse problema mantendo caracteres ASCII legíveis e representando bytes especiais com sequências iniciadas por =. O módulo quopri no Python implementa essa codificação e sua decodificação para strings de bytes e arquivos binários.

Quoted-printable funciona melhor quando o conteúdo é majoritariamente texto comum e possui poucos caracteres fora do conjunto imprimível. Quando os dados são binários ou contêm muitos bytes especiais, Base64 costuma ser menor e mais previsível. Neste guia você aprenderá a escolher o formato, trabalhar com streams, tratar cabeçalhos, preservar encodings e evitar erros de segurança.

O tema complementa nossos guias sobre mimetypes, fileinput, tempfile, filecmp e shlex.

O que é quoted-printable

O formato foi criado para transportar conteúdo que é quase todo legível em ASCII, mas contém alguns bytes especiais. Letras, números e vários sinais permanecem visíveis. Outros bytes são escritos como um sinal de igual seguido por dois dígitos hexadecimais.

Olá, mundo!  →  Ol=C3=A1, mundo!

O exemplo pressupõe que o texto foi convertido para bytes UTF-8 antes da codificação. Quoted-printable não conhece Unicode: ele apenas transforma bytes.

Codificar bytes com encodestring

import quopri

texto = "Olá, Python!".encode("utf-8")
codificado = quopri.encodestring(texto)
print(codificado)

A função recebe bytes e devolve bytes. Se você possui uma string, escolha explicitamente o encoding antes de chamar a função. UTF-8 é uma opção comum, mas sistemas legados podem exigir outro formato.

Decodificar com decodestring

dados = b"Ol=C3=A1, Python!"
decodificado = quopri.decodestring(dados)
texto = decodificado.decode("utf-8")
print(texto)

A decodificação quoted-printable e a conversão de bytes para texto são etapas separadas. Primeiro remova a codificação de transporte; depois aplique o charset informado pelo protocolo ou pela aplicação.

Não confunda charset e codificação de transporte

UTF-8 descreve como caracteres viram bytes. Quoted-printable descreve como esses bytes são transportados por um canal textual. Uma mensagem pode usar charset=utf-8 e Content-Transfer-Encoding: quoted-printable ao mesmo tempo.

Decodificar apenas uma camada produz texto corrompido ou sequências hexadecimais ainda visíveis. Em integrações, registre claramente qual camada cada função recebe e devolve.

O parâmetro quotetabs

encodestring() aceita quotetabs. Quando verdadeiro, espaços e tabs internos também são codificados.

dados = b"campo  com\ttabulacao"
print(quopri.encodestring(dados, quotetabs=True))

Espaços e tabs no fim de linhas sempre precisam ser codificados, porque muitos sistemas removem whitespace final. Essa regra protege o conteúdo contra alterações silenciosas.

Quebras de linha suaves

Linhas quoted-printable possuem limite de comprimento. O encoder pode inserir um sinal de igual no fim da linha para indicar que a quebra é apenas de transporte.

parte muito longa=
continua aqui

O decoder remove a quebra suave e reconstrói a sequência original. Não processe cada linha isoladamente sem considerar essa regra.

Codificar arquivos e streams

Para grandes volumes, use quopri.encode() com objetos binários de entrada e saída.

import quopri

with open("mensagem.txt", "rb") as origem:
    with open("mensagem.qp", "wb") as destino:
        quopri.encode(origem, destino, quotetabs=False)

O processamento por stream evita manter todo o conteúdo em memória. Abra ambos os arquivos em modo binário, porque o módulo opera sobre bytes.

Decodificar arquivos

with open("mensagem.qp", "rb") as origem:
    with open("mensagem.txt", "wb") as destino:
        quopri.decode(origem, destino)

Erros de abertura e leitura podem gerar exceções de I/O. Grave em um arquivo temporário e substitua o destino somente após sucesso quando a operação precisa ser atômica.

Modo de cabeçalho

O parâmetro header=True aplica regras usadas em cabeçalhos MIME codificados. Na decodificação, underscore é interpretado como espaço.

valor = b"Relat=C3=B3rio_mensal"
print(quopri.decodestring(valor, header=True))

Não habilite esse modo para corpos de mensagens comuns. Um underscore literal no corpo deve permanecer underscore, enquanto em um cabeçalho Q-encoded ele pode representar espaço.

Use a biblioteca email para mensagens completas

quopri é uma ferramenta de baixo nível. Para analisar ou produzir mensagens de e-mail completas, prefira o pacote email, que entende cabeçalhos, multiparts, charsets, anexos e políticas.

Manipular manualmente limites MIME e cabeçalhos facilita injeções, linhas inválidas e mensagens incompatíveis. Use quopri diretamente quando você controla uma parte específica do fluxo ou precisa interoperar com um protocolo simples.

Quando escolher quoted-printable

O formato é adequado para:

  • texto com poucas letras acentuadas;
  • conteúdo que precisa permanecer parcialmente legível;
  • mensagens MIME legadas;
  • protocolos que exigem linhas ASCII;
  • diagnósticos em que a legibilidade ajuda.

Para imagens, PDFs, arquivos compactados e conteúdo com muitos bytes não ASCII, Base64 normalmente produz menos expansão.

Comparação com Base64

Base64 aumenta o tamanho de maneira relativamente constante e esconde toda a estrutura visual. Quoted-printable preserva a maior parte do ASCII, mas cada byte especial pode virar três caracteres. Um texto em idioma com muitos caracteres não ASCII pode ficar maior em quoted-printable do que em Base64.

Meça com amostras reais quando tamanho, compatibilidade e legibilidade importam.

Dados malformados

Entradas externas podem conter sinais de igual incompletos, hexadecimais inválidos, linhas gigantes ou quebras inesperadas. O decoder é tolerante em vários casos, portanto não use a ausência de exceção como prova de validade.

Defina limites de tamanho antes de decodificar, registre anomalias e valide o formato de nível superior depois da transformação.

Segurança e expansão

A decodificação não executa código, mas o resultado pode ser HTML, script, comando, arquivo ou mensagem maliciosa. Trate os bytes decodificados conforme seu contexto. Não renderize HTML de e-mail sem sanitização e não use o conteúdo como caminho, comando ou consulta.

Limite o tamanho do corpo recebido e do resultado. Mesmo uma transformação simples pode ser usada para consumir memória quando a aplicação aceita entradas enormes.

Normalização de linhas

Protocolos de e-mail usam CRLF como separador de linha. Arquivos locais podem usar LF ou CRLF. Evite converter linhas antes de terminar a decodificação, pois quebras suaves dependem da estrutura original.

Após obter os bytes do corpo, aplique a política de normalização apropriada ao conteúdo, não ao envelope de transporte.

Exemplo com charset explícito

def codificar_texto(texto: str, charset: str = "utf-8") -> bytes:
    bruto = texto.encode(charset, errors="strict")
    return quopri.encodestring(bruto, quotetabs=False)


def decodificar_texto(dados: bytes, charset: str = "utf-8") -> str:
    bruto = quopri.decodestring(dados)
    return bruto.decode(charset, errors="strict")

Usar errors="strict" evita substituir caracteres silenciosamente. A aplicação pode capturar UnicodeError e informar que o charset declarado não corresponde aos bytes.

Processamento em memória com BytesIO

from io import BytesIO

origem = BytesIO(b"texto com acento: \xc3\xa7")
destino = BytesIO()
quopri.encode(origem, destino, quotetabs=False)
resultado = destino.getvalue()

BytesIO facilita testes e integração com APIs que produzem streams sem criar arquivos temporários.

Teste de ida e volta

def test_round_trip():
    original = "ação, café e Python".encode("utf-8")
    codificado = quopri.encodestring(original)
    restaurado = quopri.decodestring(codificado)
    assert restaurado == original

Inclua espaços no fim da linha, tabs, linhas longas, bytes nulos, underscores em cabeçalhos, diferentes charsets e entradas vazias.

Integração com arquivos temporários

Ao converter arquivos em produção, escreva a saída em um arquivo temporário no mesmo sistema de arquivos. Após validar tamanho e conteúdo, faça uma substituição atômica. Isso evita deixar um destino parcialmente escrito quando o processo falha.

Interface de linha de comando

O módulo pode ser executado como script em algumas instalações, mas para automações duradouras é melhor criar uma interface explícita com argparse. Ela pode impor limites, escolher modo de cabeçalho e controlar arquivos de entrada e saída.

Erros frequentes

  • Passar str quando a API espera bytes.
  • Decodificar quoted-printable e esquecer o charset.
  • Usar header=True no corpo da mensagem.
  • Confundir underscore literal com espaço de cabeçalho.
  • Escolher quoted-printable para grandes arquivos binários.
  • Processar linhas separadamente e perder soft breaks.
  • Presumir que saída decodificada é segura.
  • Reescrever o destino diretamente sem arquivo temporário.

Boas práticas

  • Mantenha todas as etapas em bytes até conhecer o charset.
  • Use o pacote email para mensagens completas.
  • Prefira streams para arquivos grandes.
  • Limite tamanhos antes e depois da decodificação.
  • Valide o conteúdo conforme o formato final.
  • Use modo de cabeçalho apenas em Q-encoded words.
  • Teste espaços finais e linhas longas.
  • Compare com Base64 usando dados reais.

Conclusão

O módulo quopri no Python oferece uma implementação direta de quoted-printable para bytes e streams. Ele é especialmente útil em conteúdo textual que precisa atravessar canais MIME mantendo boa parte do ASCII legível.

A implementação é simples, mas o contexto exige atenção: charset e encoding de transporte são camadas diferentes, cabeçalhos usam regras próprias e a saída ainda precisa de validação. Consulte a documentação oficial de quopri e a especificação MIME da RFC 2045 para interoperabilidade completa.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Documento e caixa de entrada representando caixas de e-mail com mailbox no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    mailbox no Python: caixas de e-mail

    Aprenda mailbox no Python para ler, criar e migrar caixas Maildir, mbox e MH com locking, mensagens, flags e tratamento

    Ler mais

    Tempo de leitura: 6 minutos
    12/08/2026
    Editor de texto representando formatação com textwrap no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    textwrap no Python: formate textos

    Aprenda textwrap no Python para quebrar, preencher, encurtar, indentar e remover recuos de textos com controle de largura e espaços.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Pasta e lupa representando filtros de nomes com fnmatch no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    fnmatch no Python: filtre nomes de arquivos

    Aprenda fnmatch no Python para filtrar nomes de arquivos com curingas, controlar maiúsculas, excluir padrões e evitar confundir glob com

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor com dados binários representando arrays numéricos compactos no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    array no Python: números compactos

    Aprenda array no Python para armazenar números compactos, manipular bytes, arquivos binários e buffers com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/08/2026
    Círculo cromático representando conversões RGB, HSV e HLS com colorsys no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    colorsys no Python: RGB, HSV e HLS

    Aprenda colorsys no Python para converter cores entre RGB, HSV, HLS e YIQ, gerar paletas e evitar erros com escalas

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Ícone de configuração representando arquivos plist com plistlib no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    plistlib: leia e grave arquivos plist

    Aprenda plistlib no Python para ler e gravar arquivos plist XML e binários, validar dados e integrar configurações Apple com

    Ler mais

    Tempo de leitura: 7 minutos
    08/08/2026