最近赋闲在家闲的蛋疼,找工作也不顺利,就安静下来学一些常用开源项目,在翻struts2的时候看到读取properties配置文件是自己定义的reader来读取,因为之前上班的时候常常使用到properties的读写,对于jdk本身的properties在保存的时候会把注释忽略掉这点深恶痛绝,一直想重新写一个properties文件读写的工具类,但是大致翻了一下properties的代码和文档,发现properties的规则挺多,没有几天时间怕是难以完成就一直搁下了。这次看到struts2的代码就想拿来借鉴一下,于是就把properties的东西读了一遍,发觉很多东西是之前忽略甚至不知道的,于是记下和兄弟们共享,如有错欢迎指正,概念颇多,容易晕头,建议找头脑清醒的时候看。

JDK Properties核心在读取配置文件的

Java代码

privatevoidload0 (LineReader lr)throwsIOException

private void load0 (LineReader lr) throws IOException

方法上。其中传入的参数LineReader类是Properties的内部类,用来读取一个逻辑行(这儿就不详细介绍了,它会读取一个逻辑行并且忽略掉逻辑行行首的所有空白字符和换行字符)。

load0方法的JDK文档总结如下,这也是后续的几个重要的概念的出处:

1.注释符为:'#'或者'!'。空白字符为:' ', '\t', '\f'。key/value分隔符为:'='或者':'。行分隔符为:'\r','\n','\r\n'。

2.自然行是使用行分隔符或者流的结尾来分割的行。逻辑行可能分割到多个自然行中,使用反斜杠'\'来连接多个自然行。

3.注释行是使用注释符作为首个非空白字符的自然行。

4.空白字符的自然行会被认为是空行而被忽略。

5.properties文件的key为从首个非空白字符开始直到(但不包括)首个非转义的'=', ':'或者非行结束符的空白字符为止。

6.key后面的第一个非空白字符如果是”=”或者”:”,那么这个字符后面所有空白字符都会被忽略掉。

7.可以使用转义序列表示key和value(当然此处的字符转义序列和unicode的转义有一些差别,jdk文档都有列出来)。

properties是一个包含了key、value对的文本文档,key,value的界定是正确读取properties的关键,那么key、value是如何界定的呢?上面第5点是对key的不完全界定但是并未涉及到value,这些,都只有从源码当中来寻找答案。

load0源码和注解如下:

Java代码

privatevoidload0(LineReader lr)throwsIOException {

char[] convtBuf =newchar[1024];

//行的长度

intlimit;

//key的长度

intkeyLen;

//value的开始点

intvalueStart;

//当前读取的字符

charc;

//是否是key/value的分隔符

booleanhasSep;

//前一个字符是否是反斜杠

booleanprecedingBackslash;

//把通过LineReader读取来的逻辑行进行遍历,一个个char的进行处理。

while((limit = lr.readLine()) >=0) {

c =0;

keyLen =0;

valueStart = limit;

hasSep =false;

precedingBackslash =false;

//循环获取key的长度

while(keyLen

c = lr.lineBuf[keyLen];

//当字符为key/value分隔符:'='或':'并且前一个字符不是反斜杠的时候,key长度读取结束,并且把hasSep设置为true,break。

if((c =='='|| c ==':') && !precedingBackslash) {

valueStart = keyLen +1;

hasSep =true;

break;

}

//当字符为空白字符' '或'\t'或'\f'并且前一个字符不是反斜杠的时候,key长度读取结束,break。

elseif((c ==' '|| c =='\t'|| c =='\f') && !precedingBackslash) {

valueStart = keyLen +1;

break;

}

//当连续存在奇数个反斜杠的时候, precedingBackslash为true。

if(c =='\\') {

precedingBackslash = !precedingBackslash;

}else{

precedingBackslash =false;

}

keyLen++;

}

//循环获取value开始的位置

while(valueStart

c = lr.lineBuf[valueStart];

//如果字符不为所有的空白字符:' ', '\t', '\f'的时候

if(c !=' '&& c !='\t'&& c !='\f') {

//如果前面不是key/value的分隔符,而是空白字符,而该字符是key/value分隔符

if(!hasSep && (c =='='|| c ==':')) {

hasSep =true;

}else{

//结束读取

break;

}

}

valueStart++;

}

//loadConvert是进行字符串转义的方法,就不用关心了。

String key = loadConvert(lr.lineBuf,0, keyLen, convtBuf);

String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);

put(key, value);

}

}

private void load0(LineReader lr) throws IOException {

char[] convtBuf = new char[1024];

//行的长度

int limit;

//key的长度

int keyLen;

//value的开始点

int valueStart;

//当前读取的字符

char c;

//是否是key/value的分隔符

boolean hasSep;

//前一个字符是否是反斜杠

boolean precedingBackslash;

//把通过LineReader读取来的逻辑行进行遍历,一个个char的进行处理。

while ((limit = lr.readLine()) >= 0) {

c = 0;

keyLen = 0;

valueStart = limit;

hasSep = false;

precedingBackslash = false;

//循环获取key的长度

while (keyLen < limit) {

c = lr.lineBuf[keyLen];

//当字符为key/value分隔符:'='或':'并且前一个字符不是反斜杠的时候,key长度读取结束,并且把hasSep设置为true,break。

if ((c == '=' || c == ':') && !precedingBackslash) {

valueStart = keyLen + 1;

hasSep = true;

break;

}

//当字符为空白字符' '或'\t'或'\f'并且前一个字符不是反斜杠的时候,key长度读取结束,break。

else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {

valueStart = keyLen + 1;

break;

}

//当连续存在奇数个反斜杠的时候, precedingBackslash为true。

if (c == '\\') {

precedingBackslash = !precedingBackslash;

} else {

precedingBackslash = false;

}

keyLen++;

}

//循环获取value开始的位置

while (valueStart < limit) {

c = lr.lineBuf[valueStart];

//如果字符不为所有的空白字符:' ', '\t', '\f'的时候

if (c != ' ' && c != '\t' && c != '\f') {

//如果前面不是key/value的分隔符,而是空白字符,而该字符是key/value分隔符

if (!hasSep && (c == '=' || c == ':')) {

hasSep = true;

} else {

//结束读取

break;

}

}

valueStart++;

}

//loadConvert是进行字符串转义的方法,就不用关心了。

String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);

String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);

put(key, value);

}

}

通过如上的代码可以看出,key/value分割符'=', ':'与空白字符:' ', '\t', '\f'是区分key、value的关键:

key的界定为:逻辑行中,从首个非空白字符开始直到(但不包括)首个非转义的'=', ':'或者非行结束符的空白字符为止。(和前面第5点基本一致)

value的界定为:逻辑行中,非转义的key/value分隔符(此处不仅仅包括'=',':',还包括' ', '\t', '\f')后面的第一个非空白字符(非' ', '\t', '\f'字符)开始到逻辑行结束的所有字符。

另外key、value还有如下特征:

1.因为LineReader是读取的逻辑行,所以key、value中可以包含多个自然行。

2.在“循环获取key的长度”的代码中可以看到处理key/value分隔符的方式和处理空白字符的方式很相似(除了在发现处理的字符为key/value分隔符的时候会把 hasSep变量设置为true)。而这表明:

如果空白字符后续没有key/value分隔符(“=”或者“:”),那么该空白字符会被当作key/value分隔符,从分隔符后的第一个非空白字符起到逻辑行结束所有的字符都当作是value。也就是说:“key1 value1”,读取出来之后的key和value分别为”key1”, “value1”。

如果空白字符后续有key/value分隔符(“=”或者“:”),那么该空白字符会被忽略,key/value分隔符后的第一个非空白字符起到逻辑行结束所有的字符都当作是value。也就是说:”key1 :value1”,读取出来之后的key和value分别为”key1”和”value1”,而不是”key1”和”:value1”。

另外,在读xwork的com.opensymphony.xwork2.util.PropertiesReader类的时候发现,它的实现和JDK的Properties实现有出入,也就是说,如果JDK的Properties是规范的话,那么xwork的properties读取类是有bug的。测试类如下(注释掉的Assert才能通过junit):

Java代码

publicclassPropertiesTest {

@Test

publicvoidtestLoad()throwsIOException {

File f =newFile(getClass().getResource(".").getPath(),"test.properties");

InputStream in =null;

try{

//java properties

in =newFileInputStream(f);

Properties props =newProperties();

props.load(in);

String s1 = props.getProperty("key");

Assert.assertEquals("value#with", s1);

String s2 = props.getProperty("comments");

Assert.assertEquals("", s2);

}finally{

if(in !=null)

try{

in.close();

}catch(IOException e) {

e.printStackTrace();

}

}

try{

//xwork properties

in =newFileInputStream(f);

Reader reader =newInputStreamReader(in);

PropertiesReader pr =newPropertiesReader(reader);

while(pr.nextProperty()) {

String name = pr.getPropertyName();

String val = pr.getPropertyValue();

if("key".equals(name)) {

Assert.assertEquals("value#with", val);

//Assert.assertEquals("valuecomments", val);

}

if("comments".equals(name)) {

Assert.assertEquals("", val);

//Assert.assertEquals(null, val);

}

}

}finally{

if(in !=null)

try{

in.close();

}catch(IOException e) {

e.printStackTrace();

}

}

}

}

public class PropertiesTest {

@Test

public void testLoad() throws IOException {

File f = new File(getClass().getResource(".").getPath(), "test.properties");

InputStream in = null;

try {

//java properties

in = new FileInputStream(f);

Properties props = new Properties();

props.load(in);

String s1 = props.getProperty("key");

Assert.assertEquals("value#with", s1);

String s2 = props.getProperty("comments");

Assert.assertEquals("", s2);

} finally {

if (in != null)

try {

in.close();

} catch (IOException e) {

e.printStackTrace();

}

}

try {

//xwork properties

in = new FileInputStream(f);

Reader reader = new InputStreamReader(in);

PropertiesReader pr = new PropertiesReader(reader);

while (pr.nextProperty()) {

String name = pr.getPropertyName();

String val = pr.getPropertyValue();

if ("key".equals(name)) {

Assert.assertEquals("value#with", val);

//Assert.assertEquals("valuecomments", val);

}

if ("comments".equals(name)) {

Assert.assertEquals("", val);

//Assert.assertEquals(null, val);

}

}

} finally {

if (in != null)

try {

in.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

test.properties的内容如下:

Java代码

key=value\

#with

comments

key=value#with

comments

好了,清楚properties的使用规则了,如果我们需要自己写一个实现在保存properties的时候注释不被忽略掉,而且按照原来的行数来保存的工具类的话,就会清晰很多了。本来想把这个工具写一下,但是写代码加调试实在太费时间,等到用的时候再来写吧。

java properties null_正确使用Java Properties - Java综合 - Java - JavaEye...相关推荐

  1. 关于java数组论述正确的是_下列关于 Java 语言的数组描述中,正确的是()。...

    下列关于 Java 语言的数组描述中,正确的是(). A:数组在赋初值和赋值时都不判界 B:数组元素是按顺序存放在内存的 C:数组的长度通常用 length 表示 D:数组下标从0开始 正确答案:数组 ...

  2. 关于java结构中描述正确的是_下列关于Java中类的构造方法的描述,正确的是()...

    A:构造函数没有返回值,也不能用void修饰.如果不小心给构造函数前面添加了返回值类型,那么这将使这个构造函数变成一个普通的方法,在运行时将产生找不到构造方法的错误. C:super(参数):调用父类 ...

  3. Java中的IO流与Properties

    IO流 1 File 1.1 File类概述和构造方法 File:它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一 ...

  4. java创建solr core_Solr定义core.properties

    核心发现意味着创建核心就像core.properties在磁盘上的文件一样简单. 该core.properties文件是一个简单的Java属性文件,其中每行只是一个key=value对,例如:name ...

  5. java properties 路径问题_Java 读取Properties文件时应注意的路径问题

    Java读取properties文件的方法比较多,网上最多的文章是"Java读取properties文件的六种方法",但在Java应用中,最常用还是通过java.lang.Clas ...

  6. java properties读取缓存_java 读取 properties文件的各种方法

    1.使用java.util.Properties类的load()方法 示例: InputStream in = lnew BufferedInputStream(new FileInputStream ...

  7. java property xml,Java开发中读取XML与properties配置文件的方法

    相关阅读: 1. XML文件: 什么是XML?XML一般是指可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言. 2.XML文件的优点: 1)XML文档内容和结构完 ...

  8. java基础50 配置文件类(Properties)

    1. 配置文件类Properties的概念 主要生产配置文件与读取配置文件的信息 2.Properties要注意的细节 1.如果配置文件一旦使用了中文,那么在使用store方法生产的配置文件额时候字符 ...

  9. java中properties类_Java中的Properties类详解

    1.Properties类是什么? Properties(Java.util.Properties),该类主要用于读取Java的配置文件,不同的编程语言有自己所支持的配置文件,配置文件中很多变量是经常 ...

最新文章

  1. R语言使用ggplot2包的快速可视化函数qplot绘制散点图(分类变量分组配色、连续值程度配色)实战
  2. AI真的会杀人?DeepMind开发了二维网格游戏来做测试
  3. window.print只打印了1页的原因
  4. java获取eureka_获取Eureka服务列表的各种场景
  5. 【Java单元测试】如何进行单元测试、异常测试、参数化测试、超时测试、测试多线程
  6. css flexbox模型_如何将Flexbox后备添加到CSS网格
  7. 批处理文件中获取当前所在路径的几种方法,以及写文件到txt
  8. 同步方案java_【Java基础】多线程中同步的两种解决方案
  9. 益智棋类游戏--走四棋儿
  10. 登录名、服务器角色、用户名和数据库角色
  11. Java包装类及自动装箱、拆箱
  12. 黑五节日营销,Facebook广告投放指南
  13. 现在流行的台式计算机搭配,现在台式电脑的主流配置有哪些?
  14. 扒了扒华为、新华三、联想、浪潮、曙光5家的财报,吓一大跳
  15. Pandas中的pivot操作
  16. 计算机专业拜年语,南昌理工学院计算机信息工程学院给您拜年啦!!
  17. Apache Hudi 从入门到放弃(2) —— MOR表的文件结构分析
  18. 转:作为一个HR,说说应届生及一两年往届生的注意情况。
  19. linux右键菜单颜色,Linux_自定义右键菜单代码详解, style !-- /*定义菜单 - phpStudy...
  20. 网页翻译软件-网页自动采集翻译软件免费

热门文章

  1. 论有一个服务器后可以干什么
  2. iOS原生混合RN开发最佳实践
  3. Python实现bp神经网络识别MNIST数据集
  4. MariaDB Audit  Statistics
  5. window7 64位 myeclipse9.0破解步骤
  6. 使用指针时的“陷阱”
  7. ftp://hxtech.com
  8. JAVA自定义Mybatis注解
  9. 任务管理器使用不了-灰色
  10. Twitter的分布式自增ID算法snowflake