properties文件在应用系统很长用,写properties文件和加载properties文件都很简单也是很常用的方法。
持久化键值对Properties类提供了store几个方法,其中只能在第一行加入注释,之前写的注释也会丢失并且不支持中文。
因此改进写一下代码,对注解中文的支持以及持久化过程中不丢失注解,不多说,上代码。
package com.zohan.www.util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
/**
* @ClassName: Properties
* @Description: 重写Properties类中的部分方法 在保存过程中不丢失注释
* @author zohan inlw@sina.com
* @date 2012-10-24 下午10:31:54
* @version 0.1.1 修改对 键值对中存在等号的
*
*/
public class Properties extends java.util.Properties {
/** 源文件地址 */
private String filePath = null;
/** 参考文件地址 */
private String referFile = null;
/**
* 存放用户放置的 key\value
*/
private Map<String, String> map = new HashMap<String, String>();
/**
* @Fields serialVersionUID :(用一句话描述这个变量表示什么)
*/
private static final long serialVersionUID = 1L;
/**
* @throws IOException
* @Title: load
* @Description: 增加load方法,
* @param file
* 设定文件
* @return void 返回类型
* @throws
*/
public void load(File file) throws IOException {
if (null != file)
this.filePath = file.getPath();
FileInputStream fis = new FileInputStream(file);
super.load(fis);
fis.close();
}
/*
* (non-Javadoc)
*
* @see java.util.Properties#getProperty(java.lang.String)
*/
@Override
public String getProperty(String key) {
String value = map.get(key);
return null == value ? super.getProperty(key) : value;
}
/*
* (non-Javadoc)
*
* @see java.util.Properties#setProperty(java.lang.String, java.lang.String)
*/
@Override
public synchronized Object setProperty(String key, String value) {
return map.put(key, value);
}
/*
* (non-Javadoc)
*
* @see java.util.Properties#stringPropertyNames()
*/
@Override
public Set<String> stringPropertyNames() {
return super.stringPropertyNames();
}
/**
*
* @Title: store
* @Description:把键值对持久化,包括注释 加载的时候请使用public void load(File file) 方法
* @param target
* @param comments
* @throws Exception
* 设定文件
* @return void 返回类型
* @throws
*/
public void store(String target, String comments) throws Exception {
// filePath 不为空且存在
File inFile = null;
String temp = System.getProperty("java.io.tmpdir");
temp = temp.endsWith(File.separator) ? temp : temp
.concat(File.separator);
if (!StringUtils.isEmpty(filePath)) {
inFile = new File(filePath);
if (inFile.exists()) {
referFile = temp.concat(inFile.getName());
}
}
inFile = new File(filePath);
// filePath 为null targetFile作为 参考文件读取
if (StringUtils.isEmpty(filePath) && !inFile.exists()) {
throw new Exception("参考文件不能为空");
}
// referFile 为空选择target 为参照文件
if (StringUtils.isEmpty(referFile)) {
referFile = temp.concat(inFile.getName());
}
FileUtils.copyFile(inFile, new File(referFile));
store0(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
target), "utf-8")), comments, true);
new File(referFile).delete();
}
/**
*
* @Title: store0
* @Description: 重写父类的写入文件方法,将注释也写入文件
* @param @param bw
* @param @param comments
* @param @param escUnicode
* @param @throws IOException 设定文件
* @return void 返回类型
* @throws
*/
private void store0(BufferedWriter bw, String comments, boolean escUnicode)
throws IOException {
if (comments != null) {
writeComments(bw, comments);
}
bw.write("#" + new Date().toString());
bw.newLine();
synchronized (this) {
Map<String, String> temp = new HashMap<String, String>();
for (Enumeration e = keys(); e.hasMoreElements();) {
String key = (String) e.nextElement();
String val = (String) get(key);
temp.put(key, val);
}
for (String key : map.keySet()) {
temp.put(key, map.get(key));
}
BufferedReader br = new BufferedReader(new FileReader(referFile));
String line = "";
while ((line = br.readLine()) != null) {
if (line.length() == 0) {
bw.newLine();
} else if (line.trim().startsWith("#")) {
writeCommentsLine(bw, line);
} else {
// 获取key(^[^=]*(\\=)?[^=]*)=
Pattern p = Pattern.compile("(^[^=]*)=");
Matcher m = p.matcher(line.replaceAll("\\\\=", "ab"));
String key = "";
if (m.find()) {
key = m.group(1);
key = line.substring(0, key.length());
}
key = key.replaceAll("\\\\=", "=");
String value = temp.remove(key.trim());
if (StringUtils.isEmpty(value)) {
String v = line.replace(key, "");
if (StringUtils.isEmpty(v)) {
value = "";
} else {
if (v.trim().startsWith("=")) {
value = v.substring(1);
} else {
value = temp.get(key);
}
}
}
key = saveConvert(key.trim(), true, escUnicode);
/*
* No need to escape embedded and trailing spaces for value,
* hence pass false to flag.
*/
value = saveConvert(value.trim(), false, escUnicode);
bw.write(key + "=" + value);
bw.newLine();
}
}
br.close();
for (String key : temp.keySet()) {
String value = map.get(key);
key = saveConvert(key.trim(), true, escUnicode);
if (!StringUtils.isEmpty(value)) {
value = saveConvert(value.trim(), false, escUnicode);
} else {
value = "";
}
bw.write(key + "=" + value);
bw.newLine();
}
}
bw.flush();
}
/*
* Converts unicodes to encoded \uxxxx and escapes special characters
* with a preceding slash
*/
private String saveConvert(String theString, boolean escapeSpace,
boolean escapeUnicode) {
int len = theString.length();
int bufLen = len * 2;
if (bufLen < 0) {
bufLen = Integer.MAX_VALUE;
}
StringBuffer outBuffer = new StringBuffer(bufLen);
for (int x = 0; x < len; x++) {
char aChar = theString.charAt(x);
// Handle common case first, selecting largest block that
// avoids the specials below
if ((aChar > 61) && (aChar < 127)) {
if (aChar == '\\') {
outBuffer.append('\\');
outBuffer.append('\\');
continue;
}
outBuffer.append(aChar);
continue;
}
switch (aChar) {
case ' ':
if (x == 0 || escapeSpace)
outBuffer.append('\\');
outBuffer.append(' ');
break;
case '\t':
outBuffer.append('\\');
outBuffer.append('t');
break;
case '\n':
outBuffer.append('\\');
outBuffer.append('n');
break;
case '\r':
outBuffer.append('\\');
outBuffer.append('r');
break;
case '\f':
outBuffer.append('\\');
outBuffer.append('f');
break;
case '=': // Fall through
case ':': // Fall through
case '#': // Fall through
case '!':
outBuffer.append('\\');
outBuffer.append(aChar);
break;
default:
if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode) {
outBuffer.append('\\');
outBuffer.append('u');
outBuffer.append(toHex((aChar >> 12) & 0xF));
outBuffer.append(toHex((aChar >> 8) & 0xF));
outBuffer.append(toHex((aChar >> 4) & 0xF));
outBuffer.append(toHex(aChar & 0xF));
} else {
outBuffer.append(aChar);
}
}
}
return outBuffer.toString();
}
/**
* Convert a nibble to a hex character
*
* @param nibble
* the nibble to convert.
*/
private static char toHex(int nibble) {
return hexDigit[(nibble & 0xF)];
}
/** A table of hex digits */
private static final char[] hexDigit = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/**
*
* @Title: writeCommentsLine
* @Description: 无特殊写法
* @param @param bw
* @param @param comments
* @param @throws IOException 设定文件
* @return void 返回类型
* @throws
*/
private static void writeCommentsLine(BufferedWriter bw, String comments)
throws IOException {
bw.write(comments);
bw.newLine();
}
/**
*
* @Title: writeComments
* @Description:拷贝类的写注解方式
* @param @param bw
* @param @param comments
* @param @throws IOException 设定文件
* @return void 返回类型
* @throws
*/
private static void writeComments(BufferedWriter bw, String comments)
throws IOException {
bw.write("#");
int len = comments.length();
int current = 0;
int last = 0;
char[] uu = new char[6];
uu[0] = '\\';
uu[1] = 'u';
while (current < len) {
char c = comments.charAt(current);
if (c > '\u00ff' || c == '\n' || c == '\r') {
if (last != current)
bw.write(comments.substring(last, current));
if (c > '\u00ff') {
uu[2] = toHex((c >> 12) & 0xf);
uu[3] = toHex((c >> 8) & 0xf);
uu[4] = toHex((c >> 4) & 0xf);
uu[5] = toHex(c & 0xf);
bw.write(new String(uu));
} else {
bw.newLine();
if (c == '\r' && current != len - 1
&& comments.charAt(current + 1) == '\n') {
current++;
}
if (current == len - 1
|| (comments.charAt(current + 1) != '#' && comments
.charAt(current + 1) != '!'))
bw.write("#");
}
last = current + 1;
}
current++;
}
if (last != current)
bw.write(comments.substring(last, current));
bw.newLine();
}
/**
* @throws Exception
* @Title: main
* @Description: 测试文件
* @param @param args 设定文件
* @return void 返回类型
* @throws
*/
public static void main(String[] args) throws Exception {
Properties pro = new Properties();
File file = new File("e:\\ss.properties");
try {
// 采用File参数
pro.load(file);
pro.setProperty("zohan", "zohan");
System.out.println(pro.get("zohan"));
// 持久化键值对
pro.store("e:\\ss.properties", null);
} catch (IOException e) {
e.printStackTrace();
}
}
}
前两天的代码里,有bug,不支持键值对中有等号,今天修复了bug重新发一次