篇章一:Loading Properties from XML
XML 属性文档具有以下 DOCTYPE 声明: <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 注意,导入或导出属性时不 访问系统 URI (http://java.sun.com/dtd/properties.dtd);该系统 URI 仅作为一个惟一标识 DTD 的字符串:
<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD for properties -->
<!ELEMENT properties ( comment?, entry* ) >
<!ATTLIST properties version CDATA #FIXED "1.0">
<!ELEMENT comment (#PCDATA) > //注释
<!ELEMENT entry (#PCDATA) > //值
<!ATTLIST entry key CDATA #REQUIRED> //key
下面给出一个实例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Hi</comment>
<entry key="foo">bar</entry>
<entry key="fu">baz</entry>
</properties>
下面运用loadFromXML(InputStream is)读取xml:
import java.util.*;
import java.io.*;
public class LoadSampleXML {
public static void main(String args[]) throws Exception {
Properties prop = new Properties();
FileInputStream fis =
new FileInputStream("sampleprops.xml");
prop.loadFromXML(fis);
prop.list(System.out);//将属性列表输出到指定的输出流
System.out.println(" The foo property: " +
prop.getProperty("foo"));
}
}
下面运用storeToXML(OutputStream os, String comment)方法来创建xml:
import java.util.*;
import java.io.*;
public class StoreXML {
public static void main(String args[]) throws Exception {
Properties prop = new Properties();
prop.setProperty("one-two", "buckle my shoe");
prop.setProperty("three-four", "shut the door");
prop.setProperty("five-six", "pick up sticks");
prop.setProperty("seven-eight", "lay them straight");
prop.setProperty("nine-ten", "a big, fat hen");
FileOutputStream fos =
new FileOutputStream("rhyme.xml");
prop.storeToXML(fos, "Rhyme");
//传递一个输出流及一个注释的String
fos.close();
}
}
输出打印的结果为:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Rhyme</comment>
<entry key="seven-eight">lay them straight</entry>
<entry key="five-six">pick up sticks</entry>
<entry key="nine-ten">a big, fat hen</entry>
<entry key="three-four">shut the door</entry>
<entry key="one-two">buckle my shoe</entry>
</properties>
为什么会选择把properties和xml关联起来?
J2SE1.5以前的版本要求直接使用XML解析器来装载配置文件并存储设置。虽然这并非是一件困难的事情,并且解析器是平台的标准部分,但是额外的工作总是有点让人烦。最近更新的java.util.Properties类现在提供了一种为程序装载和存储设置的更容易的方法:loadFromXML(InputStream is)和storeToXML(OutputStream os, String comment)方法。使用 XML 文件还是使用老式的 a=b 类型的文件完全取决于您自己。老式文件从内存的角度看肯定是轻量级的。不过,由于 XML 的普遍使用,人们会期望 XML 格式流行起来,因为它已经被广泛使用了,只不过没有用到 Properties 对象。
不过在我看来properties同xml一样,引用API中的一句话:“它将资源包中大部分(如果不是全部)特定于语言环境的信息隔离开来。”,让我们可以轻松的进行修改及动态载入。
篇章二:更好理解它的方法
在开始这一篇章的讲解之前,我们先提一下两个类:
FileInputStream类:一个文件字节输入流,从中可以读出一个字节或一批字节
FileOutputStream类:一个文件字节输出流,可向流中写一个字节或一批字节
第一个方法:public void load(InputStream inStream) throws IOException从输入流中读取属性列表(键和元素对)。
第二个方法:public void store(OutputStream out,String comments)throws IOException将load方法读取到的属性列表写入out流中。这里的comments是注释
下面我们用代码来说明:
这里我们已经创建了两个properties文件:
文件1:
#test.properties
1=a
2=b
3=c
文件二:
#temp.test.properties
today=Monday
tomorrow=Tuesday
下面我们将就Store方法进行讨论
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
public class propertiesdemo{
Properties prop = new Properties();
public static void main(String args[]){
propertiesdemo demo=new propertiesdemo();
demo.init();
}
private void init() {
try {
InputStream fis = new FileInputStream("G:/Sunny/Keta/JTest/src/com/build/test.properties");
//从输入流中读取属性列表(键和元素对)
prop.load(fis);
//调用 Hashtable 的方法 put。使用 getProperty 方法提供并行性。
//强制要求为属性的键和值使用字符串。返回值是 Hashtable 调用 put 的结果。
OutputStream fos = new FileOutputStream("G:/Sunny/Keta/JTest/src/com/build/temp.properties");
prop.setProperty("2","w");
//prop.setProperty("3","sorry");
//以适合使用 load 方法加载到 Properties 表中的格式,将此Properties 表中的属性列表(键和元素对)写入输出流
prop.store(fos, "Update '" + 2 + "' value ");
}catch (IOException e) {
e.printStackTrace();
System.err.println("Visit " + 2 + " for updating " + "w" + " value error");
}
}
}
演示代码的结果是test.properties中的内容不变
而temp.properties中的内容如下:
#Update '2' value
#Sun Dec 02 00:16:31 CST 2007
3=c
2=w
1=a
我们可以发现当我们得到test.properties的属性列表后并调用setProperty对属性列表做出修改后得到的新的属性列表覆盖了temp.properties的内容。输入顺序正如api文档所述:如果 comments 变量非 null,则首先将 ASCII # 字符、注释字符串和一个行分隔符写入输出流。因此,该 comments 可用作一个标识注释。接下来总是写入一个注释行,该行包括一个 ASCII # 字符、当前的日期和时间(就好像使用 Date 的 toString 方法获取当前时间一样)和一个由 Writer 生成的行分隔符。然后将此 Properties 表中的所有项写入 out,一次一行。
如果只想在temp.properties的后面添加而不希望覆盖 ,我们只需这样写new FileOutputStream(String name,true)即可。
下面我们将写一个读properties的模块:
Enumeration<?> enumeration = prop.propertyNames();
while (enumeration.hasMoreElements()) {
String propertyname = enumeration.nextElement().toString();
System.out.println(propertyname + ":"
+ prop.getProperty(propertyname, "unknown"));
当然在运用它时你还要把它补充完整。如果你够仔细,你将会在前面的代码中找到怎样同过key得到value的方法
篇章3:如何解决中文乱码的问题
如果你的properties文件中含有中文,在不做任何处理的情况下你得到的将是一连串的问号。在那一步中产生了这一问题呢?在API中我们可以在load方法中阅读这一段:假定该流使用 ISO 8859-1 字符编码;也就是每个字节是一个 Latin1 字符。对于非 Latin1 的字符和某些特殊字符,可以使用与字符和字符串字面值所用的类似转义序列。
也就是说我们要把汉字转换为/uXXXX。
如何做到这一点,我这里有两个办法:
办法一:使用native2ascii 命令(具体用法csdn上可以搜到很多)。
办法二:重写native2ascii命令
对于办法二,我重写了一个模块,基本实现native2ascii的转义功能:
package com.build;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
*author:Aivin Ray
*data:2007-12-01
**/
public class ToUnicode
{
// 将字符串转unicode
public static String convert(String s) {
String unicode = "";
char[] charAry = new char[s.length()];
for(int i=0; i<charAry.length; i++) {
charAry[i] = (char)s.charAt(i);
if(Character.isLetter(charAry[i])&&(charAry[i]>255))
unicode+="/u" + Integer.toString(charAry[i], 16);
else
unicode+=charAry[i];
}
return unicode;
}
//读文件
public static String readFile(String filePath) throws IOException, FileNotFoundException {
String result = null;
File file = new File(filePath);
if (file.exists()) {
FileInputStream fis = new FileInputStream(file);
byte[] b = new byte[fis.available()];
//从输入流中读取b.length长的字节并将其存储在缓冲区数组 b 中。
fis.read(b);
//使用指定的字符集解码指定的字节数组
result = new String(b, "gbk");
fis.close();
}
return convert(result);
}
}
在写这个模块之前我使用的是这个模块当然这个模块经常被用来处理jsp的中文字符问题
代码如下:
package com.build;
import java.util.ArrayList;
import java.util.PropertyResourceBundle;
import java.io.*;
/**
*author:Aivin Ray
*publish data:2007-12-1
*tips:the segment of my project
**/
public class ProperitiesRead {
private static PropertyResourceBundle Config = null;
private static final String CONFIGKEY_House_PREFIX = "HouseGrade";
ArrayList<String> list = new ArrayList<String>();
public ProperitiesRead(String file) {
setconfigfile(getClass().getResourceAsStream(file));
}
public boolean setconfigfile(InputStream in) {
boolean is = true;
if (in != null) {
try {
Config = new PropertyResourceBundle(in);
} catch (IOException e) {
// TODO 自动生成 catch 块
is = false;
}
if (is) {
try {
int index = 0;
while (true) {
String keyvalue1 = getkeyvalue(Config.getString(CONFIGKEY_House_PREFIX + index))
list1.add(keyvalue1);
index++;
}
} catch (Exception ex) {
}
}
}
return is;
}
public ArrayList<String> getHouseGrade() {
return list;
}
/*解决乱码问题,可以提取出来做为模块使用*/
public String getkeyvalue(String key) {
String keyvalue = null;
try {
keyvalue = new String(key.getBytes("ISO-8859-1"), "gb2312");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return keyvalue;
}
}
篇章4:读取Properties文件三种方法
1。使用java.util.Properties类的load()方法
示例:InputStream in=new BufferedInputStream(new FileInputStream(name));
或者 InputStream in=JProperties.class.getResourceAsStream(name);
或者 InputStream in=JProperties.class.getClassLoader().getResourceAsStream(name);
或者 InputStreamin=ClassLoader.getSystemResourceAsStream(name);
然后:
Properties p=new Properties();
p.load(in);
2。使用java.util.ResourceBundle类的getBundle()方法
示例:ResourceBundlerb=ResourceBundle.getBundle(name,Locale.getDefault());
3。使用java.util.PropertyResourceBundle类的构造函数
示例:InputStream in=new BufferedInputStream(new FileInputStream(name));
ResourceBundler b=new PropertyResourceBundle(in);
补充:
Servlet中可以使用javax.servlet.ServletContext的getResourceAsStream()方法
示例:InputStreamin=context.getResourceAsStream(path);
Properties p=new Properties();
p.load(in);
篇章五:最后的模块——对properites值的修改
package com.builder;
/**
* @author Aivin.Ray
* @data 2007-12-1
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
//前一篇已介绍此方法
import com.builder.ToUnicode;
public class CreatePro {
//读文件
public static String readFile(String filePath, String parameterName) throws IOException, FileNotFoundException {
String result = null;
File file = new File(filePath);
if (file.exists()) {
FileInputStream fis = new FileInputStream(file);
byte[] b = new byte[fis.available()];
fis.read(b);
result = new String(b, "UTF-8");
fis.close();
}
return result;
}
//修改后存储
public static void saveFile(String content, String path, String fileName) throws IOException {
File f = new File(path);
if (!f.exists()) {
f.mkdirs();
}
File fn = new File(f, fileName);
FileOutputStream fos = new FileOutputStream(fn);
fos.write(content.getBytes());
fos.close();
}
//删除旧文件
public static void deleteFile(String path) throws IOException {
File f = new File(path);
if (f.exists()) {
f.delete();
} else {
throw new IOException("未找到相关文件");
}
}
//执行方法
public static boolean writeProperties(String filePath, String parameterName, String parameterValue) {
boolean flag = false;
try {
//取得文件所有内容
String all = CreatePro.readFile(filePath, parameterName);
String result = null;
//如果配置文件里存在parameterName
if (all.indexOf(parameterName) != -1) {
//得到parameterName前的字节数
int a=all.indexOf(parameterName);
//取得以前parameterName的值
String old = readProperties(filePath, parameterName);
//得到parameterName值前的字节数
int b=a+(parameterName.length()+"=".length());
//新的properties文件所有内容为:旧的properties文件中内容parameterName+"="+新的parameterName值(parameterValue)+旧的properties文件中parameterName值之后的内容
result=all.substring(0,a)+parameterName+"="+parameterValue+all.substring(b+ToUnicode.convert(old).length(),all.length());
}
//删除以前的properties文件
CreatePro.deleteFile(filePath);
//存储新文件到以前位置
String[] arrPath = filePath.split("WEB-INF");
String path = arrPath[0]+"WEB-INF/configs";
CreatePro.saveFile(result, path, "xf.properties");
flag=true;
} catch (IOException e) {
e.printStackTrace();
flag=false;
}
return flag;
}
//读properties文件,Properties方法
public static String readProperties(String filePath, String parameterName) {
String value = "";
Properties prop = new Properties();
try {
InputStream fis = new FileInputStream(filePath);
prop.load(fis);
value = prop.getProperty(parameterName);
} catch (IOException e) {
e.printStackTrace();
}
return value;
}
这段代码是极其有意义的,它非常全面的提供了Properties的修改操作,同时删除了重复的键值。