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 [email protected] * @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重新发一次