`
what?
  • 浏览: 3101 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

改进java.util.Properties类,让输出格式与输入格式保持不变.

 
阅读更多

文章来自http://www.iteye.com/topic/156474

如果要对一个属性设置文件.properties文件进行操作,我们当然想到用java.util.Properties类.对于只进行属性读取, 这个类已经足够好了,但需要在Properties实例改变之后调用.store()或者.save()需要重新输出的时候,这个类就只会简单的把所有的 属性简单的输出,整个文件格式和属性的顺序都改变了,原来的注释也消失了.

附件中是一个改进过的properites文件读写类,在 java.util.Properties的基础上进行改进,在读取properties文件的时候把注释和顺序格式都记录下来,操作时候也把添加顺序记 录了,所以很好的解决了java.util.Properties类在输出的不足,同时它提供addComment()方法,可以添加注释,这样,经过处 理的properties的可读性就能继续保持下来了.

看下面的示范代码:

       FileInputStream input = new FileInputStream( "e:/input.properties" );

       SafeProperties safeProp = new SafeProperties();

       safeProp.load(input);

       input.close();

       safeProp.addComment( "New Comment" );

       safeProp.put( "New-Key" , "New====Value" );

       FileOutputStream output = new FileOutputStream( "e:/output.properties" );

       safeProp.store(output, null );

       output.close();


很感谢你的这个class,我正需要这样一个类来处理我的Properties文件。
不过,我在使用的时候还是发现有一个问题,就是如果重新设置原有属性的值时后,程序不是在原来的位置修改属性,而是将原有属性删除,再作为新属性添加,这样属性的位置就发生了变动。稍微修改你的类就可以解决这个问题:
1、首先修改remove方法:

 

Java代码  收藏代码
  1. public   int  remove(String key) {  
  2.     for  ( int  index =  0 ; index < commentOrEntrys.size(); index++) {  
  3.     Object obj = commentOrEntrys.get(index);  
  4.     if  (obj  instanceof  PropertyEntry) {  
  5.         if  (obj !=  null ) {  
  6.         if  (key.equals(((PropertyEntry) obj).getKey())) {  
  7.                             commentOrEntrys.remove(obj);  
  8.                             return  index;  
  9.                         }  
  10.                     }  
  11.                 }  
  12.             }  
  13.             return  commentOrEntrys.size();  
  14.         }  



2、修补putOrUpdate方法:

Java代码  收藏代码
  1. public   void  putOrUpdate(String key, String value) {  
  2.     PropertyEntry pe = new  PropertyEntry(key, value);  
  3.     int  index = remove(key);  
  4.     commentOrEntrys.add(index,pe);   

 

 

第二篇文章:http://www.iteye.com/topic/883695

 

 

 

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

Java代码  收藏代码
  1. 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代码  收藏代码
  1. private   void  load0(LineReader lr)  throws  IOException {  
  2.         char [] convtBuf =  new   char [ 1024 ];  
  3.         //行的长度   
  4.         int  limit;  
  5.         //key的长度   
  6.         int  keyLen;  
  7.         //value的开始点   
  8.         int  valueStart;  
  9.         //当前读取的字符   
  10.         char  c;  
  11.         //是否是key/value的分隔符   
  12.         boolean  hasSep;  
  13.         //前一个字符是否是反斜杠   
  14.         boolean  precedingBackslash;  
  15.         //把通过LineReader读取来的逻辑行进行遍历,一个个char的进行处理。   
  16.         while  ((limit = lr.readLine()) >=  0 ) {  
  17.             c = 0 ;  
  18.             keyLen = 0 ;  
  19.             valueStart = limit;  
  20.             hasSep = false ;  
  21.             precedingBackslash = false ;  
  22.             //循环获取key的长度   
  23.             while  (keyLen < limit) {  
  24.                 c = lr.lineBuf[keyLen];  
  25.                 //当字符为key/value分隔符:'='或':'并且前一个字符不是反斜杠的时候,key长度读取结束,并且把hasSep设置为true,break。   
  26.                 if  ((c ==  '='  || c ==  ':' ) && !precedingBackslash) {  
  27.                     valueStart = keyLen + 1 ;  
  28.                     hasSep = true ;  
  29.                     break ;  
  30.                 }  
  31.                 //当字符为空白字符' '或'\t'或'\f'并且前一个字符不是反斜杠的时候,key长度读取结束,break。   
  32.                 else   if  ((c ==  ' '  || c ==  '\t'  || c ==  '\f' ) && !precedingBackslash) {  
  33.                     valueStart = keyLen + 1 ;  
  34.                     break ;  
  35.                 }  
  36.                 //当连续存在奇数个反斜杠的时候, precedingBackslash为true。   
  37.                 if  (c ==  '\\' ) {  
  38.                     precedingBackslash = !precedingBackslash;  
  39.                 } else  {  
  40.                     precedingBackslash = false ;  
  41.                 }  
  42.                 keyLen++;  
  43.             }  
  44.             //循环获取value开始的位置   
  45.             while  (valueStart < limit) {  
  46.                 c = lr.lineBuf[valueStart];  
  47.                 //如果字符不为所有的空白字符:' ', '\t', '\f'的时候   
  48.                 if  (c !=  ' '  && c !=  '\t'  && c !=  '\f' ) {  
  49.                     //如果前面不是key/value的分隔符,而是空白字符,而该字符是key/value分隔符   
  50.                     if  (!hasSep && (c ==  '='  || c ==  ':' )) {  
  51.                         hasSep = true ;  
  52.                     } else  {  
  53.                         //结束读取   
  54.                         break ;  
  55.                     }  
  56.                 }  
  57.                 valueStart++;  
  58.             }  
  59.             //loadConvert是进行字符串转义的方法,就不用关心了。   
  60.             String key = loadConvert(lr.lineBuf, 0 , keyLen, convtBuf);  
  61.             String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);  
  62.             put(key, value);  
  63.         }  
  64.     }  


通过如上的代码可以看出,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代码  收藏代码
  1. public   class  PropertiesTest {  
  2.     @Test   
  3.     public   void  testLoad()  throws  IOException {  
  4.         File f = new  File(getClass().getResource( "." ).getPath(),  "test.properties" );  
  5.   
  6.         InputStream in = null ;  
  7.         try  {  
  8.             //java properties   
  9.             in = new  FileInputStream(f);  
  10.             Properties props = new  Properties();  
  11.             props.load(in);  
  12.             String s1 = props.getProperty("key" );  
  13.             Assert.assertEquals("value#with" , s1);  
  14.             String s2 = props.getProperty("comments" );  
  15.             Assert.assertEquals("" , s2);  
  16.         } finally  {  
  17.             if  (in !=  null )  
  18.                 try  {  
  19.                     in.close();  
  20.                 } catch  (IOException e) {  
  21.                     e.printStackTrace();  
  22.                 }  
  23.         }  
  24.   
  25.         try  {  
  26.             //xwork properties   
  27.             in = new  FileInputStream(f);  
  28.             Reader reader = new  InputStreamReader(in);  
  29.             PropertiesReader pr = new  PropertiesReader(reader);  
  30.             while  (pr.nextProperty()) {  
  31.                 String name = pr.getPropertyName();  
  32.                 String val = pr.getPropertyValue();  
  33.                 if  ( "key" .equals(name)) {  
  34.                     Assert.assertEquals("value#with" , val);  
  35.                     //Assert.assertEquals("valuecomments", val);   
  36.                 }  
  37.                 if  ( "comments" .equals(name)) {  
  38.                     Assert.assertEquals("" , val);  
  39.                     //Assert.assertEquals(null, val);   
  40.                 }  
  41.             }  
  42.         } finally  {  
  43.             if  (in !=  null )  
  44.                 try  {  
  45.                     in.close();  
  46.                 } catch  (IOException e) {  
  47.                     e.printStackTrace();  
  48.                 }  
  49.         }  
  50.     }  
  51. }  


test.properties的内容如下:
Java代码  收藏代码
  1. key=value\  
  2. #with  
  3. comments  



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

 

 

分享到:
评论

相关推荐

    java修改Properties文件,让输出格式与输入格式保持不变

    这篇博客“java修改Properties文件,让输出格式与输入格式保持不变”就探讨了如何解决这个问题。 首先,我们需要理解Java Properties类的默认行为。Properties类在加载和保存文件时,会按照一定的规则进行格式化,如...

    读取以及修改properties文件

    - 使用`java.util.Properties`类:这是Java提供的标准类,用于处理Properties文件。首先,我们需要加载文件到Properties对象中,然后可以通过关键字获取对应的值。 ```java Properties prop = new Properties(); ...

    java 改变Properties文件中的键值

    在Java代码中,首先需要导入`java.util.Properties`和`java.io.*`等相关的类库,以便进行读写Properties文件的操作。 2. **加载Properties文件** 使用`Properties`类的`load()`方法加载Properties文件。这个方法...

    Java internationalization basics.zip_JAVA 国际化_doc_国际化 java

    Java的国际化是通过一系列的API和设计原则来实现的,这些API包括`java.text`、`java.util`和`java.util.locale`包中的类。这些工具使得开发者能够创建可扩展且灵活的应用程序,可以轻松地处理多种语言和文化设置。 ...

    JAVA-Internationalization-tutorial.rar_JAVA 国际化

    5. **Internationalization API**:Java提供了`java.text`和`java.util`包中的API,用于实现国际化功能,如`ResourceBundle`类用于加载资源包,`Format`类家族用于格式化数据。 6. **国际化设计**:在开发阶段,...

    JDK14-internationalization-guide.pdf

    - `java.text.DecimalFormat`和`java.util.Currency`类用于处理货币格式。Java 14可能会扩展对更多国家货币和地区的支持。 - `NumberFormat`接口及其子类用于非货币数值的格式化,确保数字在不同文化背景下的显示...

    JAVA国际化

    在Java中,主要通过`java.util.ResourceBundle`类和`.properties`文件来实现这一功能。 1. **ResourceBundle**:这是Java中的核心类,用于管理应用中的本地化资源。开发者可以为每种语言或地区创建一个对应的...

    最简单的java国际化例子

    Java提供了`java.text.SimpleDateFormat`类来处理日期和时间的格式化,`java.text.NumberFormat`类处理数字,而`java.util.Currency`类则用于货币。这些类都支持根据`Locale`进行自动配置。 例如,如果我们要格式化...

    java国际化代码(jsp)

    资源包通常是`.properties`格式的文本文件,其中包含了键值对,键是不变的标识符,值是根据语言环境变化的文本。例如,一个英语版本的资源包可能包含`greeting=Hello`,而法语版本的资源包则可能是`greeting=...

    国际化 必备工具 java编写

    Java的`java.util.ResourceBundle`类是用来加载和管理这些资源文件的。通过这个类,我们可以根据用户的语言环境动态地加载相应的资源。 3. **Locale类** `java.util.Locale`类用于表示不同的地区设置,如语言、...

    java国际化&时间处理

    开发者可以通过`ResourceBundle`类加载根据不同地区定制的资源文件,这些文件通常以`.properties`格式存储,包含键值对,键是不变的,值是对应语言环境的文本。 在Java中,`java.text`包提供了用于格式化和解析日期...

    Java 程序国际化教程+源码

    `Locale`类包含了与特定地区相关的数据,如日期和数字的格式。 在Java中,资源包(Resource Bundle)是实现国际化的核心。资源包是一系列键值对,存储了应用程序中的文本和其他可变内容,比如错误消息和用户界面...

    java国际化

    这些类可以创建与`Locale`相关的实例,以确保输出符合用户所在地区的标准格式。 总的来说,Java的国际化机制提供了强大的工具,使开发者能够轻松地创建多语言应用。通过`Locale`和`ResourceBundle`,我们可以实现对...

    java socket实现smtp发送邮件,支持SSL

    import java.util.Properties; ``` 2. 设置SMTP服务器的属性,包括主机名、端口、身份验证等: ```java Properties props = new Properties(); props.put("mail.smtp.host", "smtp.qq.com"); // QQ邮箱的SMTP...

    java程序多国语言示例

    Java提供了`java.util.ResourceBundle`类来管理和加载这些资源包。以下是一些关于Java多国语言支持的关键知识点: 1. **Resource Bundle创建**:首先,你需要为每种语言创建一个.properties文件,例如`messages_en....

    JAVA汉化工具

    2. **国际化(i18n)支持**:Java提供了强大的国际化支持,通过`java.util.ResourceBundle`类来管理不同语言的资源。开发者需要在代码中适当地引用这些资源,以便根据用户的系统语言自动加载相应的文本。 3. **编码...

    Java国际化编程及其实现.rar

    在Java中,国际化主要通过java.util包下的Locale、ResourceBundle和Format类来实现。 首先,Locale类代表一个特定的语言环境,包括语言、国家和变体。例如,"zh_CN"代表简体中文,"en_US"代表美式英语。开发者可以...

    java国际化组件

    首先,我们来看一下Java的`java.util.Locale`类。Locale对象用于表示语言、国家和地区,例如"zh_CN"代表简体中文,"en_US"代表美国英语。开发者可以根据用户的系统设置或者用户的选择动态地切换Locale,从而提供相应...

Global site tag (gtag.js) - Google Analytics