Java .properties 解析器
.properties 看起来是最简单的配置格式,却也是手写解析器最容易搞错的格式。本页把 java.util.Properties.load 真正使用的流程(JDK 的 LineReader,然后是 load0 和 loadConvert)跑在你粘贴或拖入的文本上,逐个键显示解析结果,并给出 JSON 视图和 Properties.store 会写回的文件。 分隔符是 =、: 或一段空白,所以 "c three" 是键 c、值 three,"key with spaces=x" 的键是 "key"、值是 "with spaces=x"。分隔符两侧的空格会消失,值尾部的空格不会,而且编辑器里看不出来。行尾的反斜杠会把下一行接上来并去掉其缩进,但偶数个反斜杠根本不是续行,以反斜杠结尾的注释行也不会续行。# 和 ! 只有作为一行的第一个非空白字符时才是注释,因此 URL 片段里的 # 会留在值里。转义只有 \t \n \r \f 和 \uXXXX,其余反斜杠都会被悄悄丢掉。 这个格式在现实中最大的坑是编码。几乎所有框架调用的 load(InputStream) 都按 ISO-8859-1 解码字节,因此以 UTF-8 保存的文件会把一个带重音的字符变成两三个。当输入含有非 ASCII 字符时,本页会同时给出两种读法并指出哪些键会变,方便你在上线前决定用 \uXXXX 转义还是用指定字符集的 load(Reader)。 全部处理都在浏览器内完成,不会上传任何内容。它无法告诉你重复键中哪一个才是你想要的,也无法判断看起来奇怪的值是否真的写错了:它报告的是 JDK 对这些字节会做什么,而不是你本来想写什么。
唯一键
11
赋值次数
12
注释行
1
续行条目
1
提示
6
几乎所有框架默认使用的 load(InputStream) 始终按 ISO-8859-1 读取。切换两种读法即可看出哪些键会变。
非 ASCII 字符会乱码 · 行 10
load(InputStream) 按 ISO-8859-1 解码字节,因此以 UTF-8 保存的文件会把一个字符变成两三个拉丁字符。请写成 \uXXXX,或用指定字符集的 Reader 读取。
- greeting: "Grüße" → "GrüÃe"
空白作为分隔符 · 行 4
这一行没有 = 也没有 :,第一段空格或制表符把键和值分开,所以 "c three" 就是 c = three。
app.mode
值尾部空格被保留 · 行 9
分隔符两侧的空格会被丢弃,但值尾部的空格属于值本身。编辑器看不到,也不会报错。
app.token (+3)
键里含有分隔符 · 行 6
这个键里有被转义的 =、: 或空格,所以整行只是一个键。用第一个 "=" 切分的代码会切错位置。
menu.label 1
这不是注释 · 行 14
# 和 ! 只有作为一行的第一个非空白字符时才开启注释。这里它在值的后面,因此留在值里。
note
重复的键 · 行 2, 12
同一个键被赋值多次。load 只保留最后一个值且不做任何提示,前面几行是看起来还在生效的死配置。
app.name → "orders-api-v2"
| 行 | 键 | 值 | 分隔符 | 备注 |
|---|---|---|---|---|
| 1 | # Spring Boot style configuration — and every trap in the format | 注释 | ||
| 2 | app.name | orders-api | = | 被覆盖 |
| 3 | server.port | 8080 | : | |
| 4 | app.mode | production | 空白 | 空白作为分隔符 |
| 5 | jdbc.url | jdbc:postgresql://db:5432/orders?ssl=true | = | |
| 6 | menu.label 1 | Save as… | = | 键里含有分隔符 |
| 7–8 | welcome.message | Hello and welcome | = | |
| 9 | app.token | abc123··· | = | 值尾部空格被保留 |
| 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、\t,并用 · 标出值尾部的空格;JSON 视图则是原样值。
使用方法
- 把文件内容粘贴到输入框,或把 .properties 文件拖到虚线区域:文件只在浏览器里读取,不会上传。
- 看逐行表格:每个条目来自第几行、实际使用的分隔符是什么,以及该行有什么不寻常之处的标记。
- 逐条处理上方的提示。重复键、值尾部空格、吞掉下一条目的续行,这三项最容易在生产环境悄悄改变行为。
- 如果文件包含非 ASCII 字符,在 UTF-8 与 ISO-8859-1 两种读法之间切换,看清 load(InputStream) 会交给应用什么内容。
- 需要做配置对比就复制 JSON 视图;需要写回文件就复制 Properties.store 视图,其中分隔符和非 ASCII 字符都已转义。
常见问题
- "c three" 里没有等号,为什么被拆成键和值?
- 因为在这个格式里空白也是分隔符。Properties.load 会寻找第一个未转义的 =、: 或空白字符,谁先出现就用谁,并把它前面的部分当作键。所以 "c three" 是键 c、值 three;"key with spaces=x" 的键是 "key"、值是 "with spaces=x",等号根本没有机会充当分隔符。如果键里确实要有空格,必须写成 "\ ",Properties.store 写回这种键时用的正是这种写法。
- 我的中文或重音字符变成了乱码,发生了什么?
- java.util.Properties.load(InputStream) 的规范就是把流按 ISO-8859-1 解码,一个字节一个字符。以 UTF-8 保存的文件里一个汉字占三个字节,这三个字节就变成三个拉丁字符,也就是典型的乱码。整个过程不会抛异常,所以坏掉的字符串会一路传到界面上。有三种修法:把非 ASCII 写成 \uXXXX(native2ascii 和 Properties.store 的做法)、用 UTF-8 的 InputStreamReader 调用 load(Reader),或者改用默认 UTF-8 的 loadFromXML。
- 值尾部的空格真的会保留吗?
- 会,而且它制造了一整类 bug 报告。行首的空白、分隔符两侧的空白、续行的缩进都会被丢掉,但值尾部的空白会原样保留,因为行到此结束,没有人再去修剪它。一个多了空格的密码或 URL 能正常解析、只是连不上,而且在编辑器和代码评审里跟正确版本看起来一模一样。本页的表格会把这些字符显示出来。
- 什么样的行会续到下一行?
- 以奇数个反斜杠结尾时。最后一个反斜杠被去掉,下一行接上来,并去掉那一行的前导空白,所以缩进的续行读起来很自然。偶数个则不是续行:它们是字面反斜杠,数量减半,条目到此结束。由此产生两个意外。续行可能吞掉看起来像下一个条目的行:如果某个值末尾多了一个反斜杠,下面那行 "key=value" 就成了这个值的一部分,而那个键根本不存在。另外,以反斜杠结尾的注释行完全不会续行,因为注释是整行丢弃的。
- 两行设置了同一个键,哪一个生效?
- 后一行,而且悄无声息。Properties 是 Hashtable,load 只是一边扫描一边为每个条目调用 put,所以后面的行会覆盖前面的行,没有警告、没有错误,也不留任何记录。当一个文件由多个片段拼装而成,或者同一个键一次用 =、一次用 : 定义时,很容易在无意中造成这种情况。本页会列出每个重复的键以及设置它的所有行号,并把落败的行变淡。
- 它和 .env 文件是同一种格式吗?
- 不是,把两者当成一回事是静默故障的常见来源。.env 没有空白分隔符,用引号保护值和容纳多行字符串,把值后面的 # 当作行内注释,而且常常支持 ${VAR} 展开。.properties 一样都没有:引号只是会进入值里的普通字符,值后面的 # 是值的一部分,格式本身没有变量展开(那是 Spring 另外加的),续行靠反斜杠而不是引号。转义也不同,.properties 只有 \t \n \r \f 和 \uXXXX。
相关工具
文本编码转换器
把 EUC-KR、Shift_JIS、Windows-1252 等非 UTF-8 文本变成可读 UTF-8。
Mbox 归档查看器
在浏览器里切分 .mbox 归档:邮件边界、解码后的头部、MIME 结构、会话线索与重复 Message-ID,全程不上传。
Java 类文件分析器
在浏览器中解析编译好的 .class 文件:类文件版本与对应的 Java 版本、访问标志、常量池、字段、方法和引用的类。
BSON 解码器 · Extended JSON 查看器
在浏览器里解码 MongoDB 的 BSON 文件:每个元素类型及其类型字节、精确的 int64 与 decimal128,以及规范或宽松的 Extended JSON。
JSON 转 Java 类(POJO)转换器
在浏览器中将 JSON 对象转换为带类型映射字段的 Java POJO 类。
字符串转义转换器
为 10 种上下文一次性转义字符串 — JavaScript、JSON、HTML 实体、URL、SQL、正则、Bash(单/双引号)、C 字符串、Unicode \u 转义。按需复制。