Analisador de Java .properties
Um arquivo .properties parece o formato de configuração mais simples que existe e é o que os parsers feitos à mão mais erram. Esta página executa o algoritmo que java.util.Properties.load realmente usa — o LineReader do JDK e depois load0 e loadConvert — sobre o texto que você colar ou soltar, e mostra o mapa resultante chave por chave, como JSON e como o arquivo que Properties.store escreveria de volta. Ela aponta exatamente as partes em que as pessoas tropeçam. O separador é =, : ou uma sequência de espaços, então "c three" é a chave c com o valor three, e "key with spaces=x" é a chave "key" com o valor "with spaces=x". O espaço em volta do separador some; o do fim do valor não some, e nenhum editor mostra. Uma barra invertida no fim da linha a continua e remove a indentação da linha seguinte, mas um número par de barras não continua nada, e uma linha de comentário terminada em barra também não. # e ! abrem comentário apenas como primeiro caractere não branco da linha, então o # de um fragmento de URL fica dentro do valor. Os únicos escapes são \t \n \r \f e \uXXXX; qualquer outra barra invertida é descartada em silêncio. O maior problema real deste formato é a codificação. O load(InputStream) — a chamada que quase todo framework faz — decodifica os bytes como ISO-8859-1, então um arquivo salvo em UTF-8 transforma cada caractere acentuado em dois ou três. Quando a entrada tem caracteres não ASCII, esta página mostra as duas leituras e nomeia as chaves que mudam, para você escolher entre escapes \uXXXX e load(Reader) com um charset explícito. Tudo roda no seu navegador e nada é enviado. O que a ferramenta não pode dizer é qual das duas chaves duplicadas você queria, nem se um valor que parece errado está errado: ela relata o que o JDK fará com esses bytes, não o que você pretendia escrever.
Chaves únicas
11
Atribuições
12
Linhas de comentário
1
Linhas continuadas
1
Achados
6
O load(InputStream) é o padrão em quase todo framework e sempre lê ISO-8859-1. Alterne entre as duas leituras para ver quais chaves mudam.
Texto não ASCII vai corromper · linha 10
O load(InputStream) decodifica os bytes como ISO-8859-1, então um arquivo salvo em UTF-8 transforma cada caractere acentuado em dois ou três latinos. Escreva-os como \uXXXX ou carregue com um Reader e um charset explícito.
- greeting: "Grüße" → "GrüÃe"
Separador de espaço · linha 4
Nesta linha não há = nem :: a primeira sequência de espaços ou tabulações separa a chave do valor, então "c three" é c = three.
app.mode
Espaços finais preservados · linha 9
O espaço em volta do separador é descartado, mas o do fim do valor faz parte do valor. Nenhum editor mostra isso e nenhum erro menciona.
app.token (+3)
Separador dentro da chave · linha 6
Esta chave contém um =, : ou espaço escapado, então a linha é uma chave só. Código que corta no primeiro "=" partiria no lugar errado.
menu.label 1
Não é um comentário · linha 14
# e ! abrem comentário apenas como primeiro caractere não branco da linha. Aqui um deles vem depois de um valor, então fica dentro do valor.
note
Chave duplicada · linha 2, 12
A mesma chave é atribuída mais de uma vez. O load mantém o último valor e não avisa nada: as linhas anteriores são configuração morta que ainda parece viva num diff.
app.name → "orders-api-v2"
| Linha | Chave | Valor | Separador | Notas |
|---|---|---|---|---|
| 1 | # Spring Boot style configuration — and every trap in the format | comentário | ||
| 2 | app.name | orders-api | = | sobrescrita |
| 3 | server.port | 8080 | : | |
| 4 | app.mode | production | espaço | Separador de espaço |
| 5 | jdbc.url | jdbc:postgresql://db:5432/orders?ssl=true | = | |
| 6 | menu.label 1 | Save as… | = | Separador dentro da chave |
| 7–8 | welcome.message | Hello and welcome | = | |
| 9 | app.token | abc123··· | = | Espaços finais preservados |
| 10 | greeting | Grüße | = | |
| 11 | greeting.escaped | Grüße | = | |
| 12 | app.name | orders-api-v2 | = | |
| 13 | path | C:\\Users\\dev\\app | = | |
| 14 | note | value # this is not a comment | = | Não é um comentário |
A tabela escapa caracteres de controle (\n, \t) e marca espaços finais com · para que fiquem visíveis; a visão JSON é exata.
Como usar
- Cole o conteúdo na caixa ou solte um arquivo .properties na área indicada: ele é lido no navegador e nunca enviado.
- Leia a tabela por linha: cada entrada com a linha de onde veio, o separador realmente usado e uma etiqueta para o que houver de incomum.
- Percorra os achados acima. Chaves duplicadas, espaço no fim do valor e uma continuação que engoliu a entrada seguinte são os três que mudam o comportamento em produção sem avisar.
- Se houver caracteres não ASCII, alterne a leitura entre UTF-8 e ISO-8859-1 para ver exatamente o que o load(InputStream) vai entregar à sua aplicação.
- Copie a visão JSON para comparar configurações, ou a visão Properties.store para obter o mesmo mapa escrito com todos os separadores e caracteres não ASCII já escapados.
Perguntas frequentes
- Por que "c three" vira chave e valor se não há um sinal de =?
- Porque neste formato o espaço em branco também é separador. O Properties.load procura o primeiro =, : ou caractere de espaço não escapado, o que vier primeiro, e toma como chave tudo o que vem antes. Por isso "c three" é a chave c com o valor three, e em "key with spaces=x" a chave é "key" e o valor é "with spaces=x": o = nunca chega a agir como separador. Se você precisa de um espaço dentro da chave, tem de escrevê-lo como "\ ", que é exatamente o que o Properties.store faz ao gravar essa chave de volta.
- Meus caracteres acentuados saem como é. O que aconteceu?
- A especificação do java.util.Properties.load(InputStream) diz que o fluxo é decodificado como ISO-8859-1, um byte por caractere. Um arquivo salvo em UTF-8 guarda é em dois bytes, e esses dois bytes viram dois caracteres latinos — o mojibake clássico. Nada é lançado, então a corrupção viaja até onde o texto for exibido. Há três correções: escrever o não ASCII como \uXXXX (o que native2ascii e Properties.store produzem), chamar load(Reader) com um InputStreamReader em UTF-8, ou usar loadFromXML, que é UTF-8 por padrão.
- O espaço no fim de um valor sobrevive mesmo?
- Sobrevive, e é a origem de um gênero inteiro de relatos de bug. O espaço no início da linha é descartado, o que cerca o separador é descartado e a indentação de uma linha de continuação também, mas o espaço no fim do valor é mantido literalmente, porque a linha acaba ali e ninguém apara. Uma senha ou URL com um espaço a mais é analisada sem erro, não conecta em nada e parece idêntica à versão boa em qualquer editor e em qualquer revisão de código. A tabela desta página marca esses caracteres para que fiquem visíveis.
- Quando uma linha continua na seguinte?
- Quando termina em um número ímpar de barras invertidas. A última barra é removida, a linha seguinte é anexada e a indentação dela é retirada, então uma continuação indentada lê-se naturalmente. Um número par não é continuação: são barras literais, pela metade, e a entrada acaba ali. Daí saem duas surpresas. Uma continuação pode engolir o que parece a próxima entrada: se um valor termina em uma barra perdida, o "chave=valor" de baixo passa a fazer parte daquele valor e a chave some. E uma linha de comentário terminada em barra não continua de jeito nenhum, porque comentários são descartados inteiros.
- Duas linhas definem a mesma chave. Qual vence?
- A última, em silêncio. Properties é uma Hashtable e o load apenas chama put para cada entrada conforme avança, então uma linha posterior sobrescreve a anterior sem aviso, sem erro e sem deixar registro. É fácil criar isso sem querer quando um arquivo é montado a partir de vários fragmentos, ou quando uma chave é definida uma vez com = e outra com : e as duas não se parecem à primeira vista. Esta página lista cada chave duplicada com todos os números de linha que a definem e esmaece as que perderam.
- É o mesmo formato de um arquivo .env?
- Não, e tratar um como o outro é fonte comum de quebras silenciosas. Um .env não tem separador por espaço, usa aspas para proteger valores e guardar textos de várias linhas, trata um # depois do valor como comentário na mesma linha e muitas vezes suporta expansão ${VAR}. Um .properties não tem nada disso: aspas são caracteres comuns que acabam dentro do valor, o # depois de um valor faz parte do valor, o formato em si não expande variáveis (o Spring acrescenta a dele por cima) e a continuação é feita com barras invertidas, não com aspas.
Ferramentas relacionadas
Conversor de Codificação de Texto
Abra arquivos em codificações antigas (EUC-KR, Shift_JIS, Windows-1252…) como UTF-8 legível.
Visualizador de arquivos mbox
Divide um arquivo .mbox no navegador: limites de mensagem, cabeçalhos decodificados, estrutura MIME, tópicos e Message-ID duplicados. Nada é enviado.
Inspetor de arquivos .class do Java
Analise um .class compilado no navegador: versão do arquivo e do Java, sinalizadores de acesso, pool de constantes, campos, métodos e dependências.
Decodificador BSON e visualizador de Extended JSON
Decodifique um arquivo BSON do MongoDB no navegador: cada tipo de elemento com seu byte, int64 e decimal128 exatos, e Extended JSON canônico ou relaxado.
Conversor de JSON para classe Java (POJO)
Transforme um objeto JSON em classes POJO Java com campos tipados, no seu navegador.
Conversor de escape de strings
Escapa uma string para 10 contextos de uma vez — JavaScript, JSON, entidades HTML, URL, SQL, regex, Bash (aspas simples/duplas), C string, escapes Unicode \u.