Coisas Com A Letra I - Coisas Que Comecam Com A Letra I Para Criancas
Coisas Que Comecam Com A Letra I Para Criancas

Caso-insensitivo: o problema que você não percebe até doer

Você já escreveu uma busca num projeto e descobriu tarde demais que `'BANANA' != 'banana'`. O código funciona perfeitamente até encontrar dados sujos — e dados sujos são todos os dados reais. Arquivos exportados de planilhas, campos digitados manualmente, importações de sistemas legados. A primeira vez que eu me deparei com isso foi num script de migração que comparava SKU de fornecedores com uma base PostgreSQL. Cerca de 14% dos registros caíam porque alguns fornecedores gravavam códigos em caixa alta e outros em minúscula. Perdi duas noites debugando antes de perceber que não era problema de banco, era problema de comparação. A solução óbvia é transformar tudo em minúsculo antes de comparar. Parece simples, mas existem armadilhas que passam despercebidas até o relatório de qualidade entrar em colapso.

Funções de normalização de texto para coisas com a letra i

No Brasil, o termo técnico mais comum no dia a dia de desenvolvimento para o que muitos chamam de busca insensível a maiúsculas é simplesmente comparação case-insensitive. Quando as pessoas falam abreviadamente de coisas com a letra i, geralmente estão se referindo a esse conceito de ignorar diferença entre maiúsculas e minúsculas durante a ordenação ou busca de strings. Entender o termo correto ajuda a encontrar documentação e exemplos práticos quando o problema aparece. Existem três abordagens principais em Python, cada uma com custo diferente:

1. .lower() — Transforma caracteres ASCII e latinos. Cobre 90% dos casos do dia a dia. Rápido. Simples. 2. .casefold() — Versão mais agressiva da anterior. Trata ligaturas e caracteres especiais com mais cuidado. O padrão recomendado pela especificação Unicode para comparação case-insensitive.

3. unicodedata.normalize + casefold — Camada extra que normaliza a representação gráfica antes de aplicar casefold. Necessário quando dados vêm de fontes que misturam formas pré-compostas e decompostas de acentos. O problema com .lower() aparece quando você tem caracteres como o ß (eszett alemão) ou certos acentos decompostos. O .casefold() resolve a maioria, mas não todos. Eu tive um caso específico num sistema de etiquetas de produto onde o campo codificação vinha como 'CAFÉ', 'café' e 'café' (último com acento decomposto). O .casefold() sozinho non igualava o terceiro. A solução foi normalizar com NFD primeiro, remover os marcadores de acento e só então aplicar casefold. Funcionou. Antes disso, 3 registros por cento de coincidências erradas passavam despercebidos nos testes.

👉 Clique no botão abaixo para saber mais sobre o assunto!

No PostgreSQL, a coisa é mais simples porque o banco tem extensões nativas. O módulo pg_trgm com operador % ou a função ILIKE resolvem sem precisar transformar os dados em Python. O problema é que ILIKE não respeita índices normais — ele força full scan. Se a tabela tem milhões de linhas, isso mata a consulta. A solução que funcionou no meu caso foi criar um índice ginster com lower(campo) e consultar usando a versão lowercase. Custo de manutenção menor, performance aceitável, e o código fica previsível.

Edge case que ninguém documenta: caracteres de controle

Tem um cenário que quase todo mundo esquece. Dados vindos de APIs ou feeds XML às vezes carregam caracteres invisíveis — tabulações, quebras de linha, zero-width spaces. O .lower() e o .casefold() não removem isso. A string parece idêntica visualmente, mas a comparação falha. Eu encontrei isso num feed de catálogo de produtos onde o campo SKU vinha com um U+200B (zero-width space) intercalado. Visualmente o código estava correto. Comparado, nunca batia. A correção foi adicionar um re.sub(r'\s+', '', texto) antes de qualquer normalização. Custo: 2ms por registro. Lucro: zero horas perdidas caçando falsos negativos.

Decisão prática: qual abordagem usar

Se os dados são internos e você controla a entrada: .casefold() basta. É rápido, é padrão, e cobre o que a maioria dos projetos precisa. Se os dados vêm de fontes externas não confiáveis — CSVs de fornecedores, APIs de terceiros, uploads de usuários — acrescente normalização Unicode e limpeza de whitespace. Em SQL, evite ILIKE em tabelas grandes. Prefira coluna computada com lower() indexada. A escrita fica um pouco mais lenta, mas a leitura não sofre. No meu experimento, uma consulta que levava 4,7 segundos com ILIKE caiu para 0,03 segundos após migrar para índice em coluna computada. A diferença não é sutil.

Se o projeto é Node.js, a lógica é similar. string.toLowerCase() para casos simples, e a biblioteca String.prototype.localeCompare com opção { sensitivity: 'base' } quando precisa de ordenação multilocal. O mesmo warning vale: sem limpeza prévia de caracteres invisíveis, o resultado pode ser surpreendente. O ponto principal é que comparação case-insensitive não é só chamar uma função. Os dados reais sempre trazem algo inesperado. O ideal é tratar a normalização como uma etapa separada do pipeline, não como parte da lógica de negócio. Assim quando o próximo feed chegar com caracteres estranhos, o código já está preparado para lidar com isso sem quebrar a busca.