java读写properties配置文件不改变属性的顺序和注释
先贴代码
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set; /**
* 扩展properties工具类
*
* @author tangming
* @date 2017-11-10
*/
public class SafeProperties { /**
* 内部属性表
*/
private final Properties props; /**
* 保存key与comment的映射, 同时利用这个映射来保证key的顺序。
*/
private final LinkedHashMap<String, String> keyCommentMap = new LinkedHashMap<String, String>(); private static final String BLANK = ""; public SafeProperties() {
super();
props = new Properties();
} public SafeProperties(Properties defaults) {
super();
props = new Properties(defaults);
} /**
* 设置一个属性,如果key已经存在,那么将其对应value值覆盖。
*
* @param key
* @param value
* @return
*/
public String setProperty(String key, String value) {
return setProperty(key, value, BLANK);
} /**
* 设置一个属性,如果key已经存在,那么将其对应value值覆盖。
*
* @param key 键
* @param value 与键对应的值
* @param comment 对键值对的说明
* @return
*/
public synchronized String setProperty(String key, String value, String comment) {
Object oldValue = props.setProperty(key, value);
if (BLANK.equals(comment)) {
if (!keyCommentMap.containsKey(key)) {
keyCommentMap.put(key, comment);
}
} else {
keyCommentMap.put(key, comment);
}
return (String) oldValue;
} /**
* 根据key获取属性表中相应的value。
*
* @param key
* @return
*/
public String getProperty(String key) {
return props.getProperty(key);
} /**
* 根据key获取属性表中相应的value。 如果没找到相应的value,返回defaultValue。
*
* @param key
* @param defaultValue
* @return
*/
public String getProperty(String key, String defaultValue) {
return props.getProperty(key, defaultValue);
} /**
* 从一个字符流中读取属性到属性表中
*
* @param reader
* @throws IOException
*/
public synchronized void load(Reader reader) throws IOException {
load0(new LineReader(reader));
} /**
* 从一个字节流中读取属性到属性表中
*
* @param inStream
* @throws IOException
*/
public synchronized void load(InputStream inStream) throws IOException {
load0(new LineReader(inStream));
} /**
* 从一个字节流中读取属性到属性表中
*
* @param inStream
* @param charset
* @throws IOException
*/
public synchronized void load(InputStream inStream, String charset) throws IOException {
InputStreamReader reader = new InputStreamReader(inStream, charset);
load0(new LineReader(reader));
} /**
* 从一个文件中读取属性到属性表中
*
* @param file 属性文件
* @param charset 字符集
* @throws IOException
*/
public synchronized void load(File file, String charset) throws IOException {
FileInputStream inputStream = new FileInputStream(file);
InputStreamReader reader = new InputStreamReader(inputStream, charset);
load0(new LineReader(reader));
} /**
* 从一个文件中读取属性到属性表中 默认字符集为utf-8
*
* @param file 属性文件
* @throws IOException
*/
public synchronized void load(File file) throws IOException {
FileInputStream inputStream = new FileInputStream(file);
InputStreamReader reader = new InputStreamReader(inputStream, "utf-8");
load0(new LineReader(reader));
} /**
* 将属性表中的属性写到字符流里面。
*
* @param writer
* @throws IOException
*/
public void store(Writer writer) throws IOException {
store0((writer instanceof BufferedWriter) ? (BufferedWriter) writer : new BufferedWriter(writer), false);
} /**
* 将属性表中的属性写到字节流里面。
*
* @param out
* @throws IOException
*/
public void store(OutputStream out) throws IOException {
store0(new BufferedWriter(new OutputStreamWriter(out, "utf-8")), true);
} /**
* 如果属性表中某个key对应的value值和参数value相同 那么返回true,否则返回false。
*
* @param value
* @return
*/
public boolean containsValue(String value) {
return props.containsValue(value);
} /**
* 如果属性表中存在参数key,返回true,否则返回false。
*
* @param key
* @return
*/
public boolean containsKey(String key) {
return props.containsKey(key);
} /**
* 获取属性表中键值对数量
*
* @return
*/
public int size() {
return props.size();
} /**
* 检查属性表是否为空
*
* @return
*/
public boolean isEmpty() {
return props.isEmpty();
} /**
* 清空属性表
*/
public synchronized void clear() {
props.clear();
keyCommentMap.clear();
} /**
* 获取属性表中所有key的集合。
*
* @return
*/
public Set<String> propertyNames() {
return props.stringPropertyNames();
} public synchronized String toString() {
StringBuffer buffer = new StringBuffer();
Iterator<Map.Entry<String, String>> kvIter = keyCommentMap.entrySet().iterator();
buffer.append("[");
while (kvIter.hasNext()) {
buffer.append("{");
Map.Entry<String, String> entry = kvIter.next();
String key = entry.getKey();
String val = getProperty(key);
String comment = entry.getValue();
buffer.append("key=" + key + ",value=" + val + ",comment=" + comment);
buffer.append("}");
}
buffer.append("]");
return buffer.toString();
} public boolean equals(Object o) {
// 不考虑注释说明是否相同
return props.equals(o);
} public int hashCode() {
return props.hashCode();
} private void load0(LineReader lr) throws IOException {
char[] convtBuf = new char[1024];
int limit;
int keyLen;
int valueStart;
char c;
boolean hasSep;
boolean precedingBackslash;
StringBuffer buffer = new StringBuffer(); while ((limit = lr.readLine()) >= 0) {
c = 0;
keyLen = 0;
valueStart = limit;
hasSep = false;
// 获取注释
c = lr.lineBuf[keyLen];
if (c == '#' || c == '!') {
String comment = loadConvert(lr.lineBuf, 1, limit - 1, convtBuf);
if (buffer.length() > 0) {
buffer.append("\n");
}
buffer.append(comment);
continue;
}
precedingBackslash = false;
while (keyLen < limit) {
c = lr.lineBuf[keyLen];
// need check if escaped.
if ((c == '=' || c == ':') && !precedingBackslash) {
valueStart = keyLen + 1;
hasSep = true;
break;
} else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
valueStart = keyLen + 1;
break;
}
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
keyLen++;
}
while (valueStart < limit) {
c = lr.lineBuf[valueStart];
if (c != ' ' && c != '\t' && c != '\f') {
if (!hasSep && (c == '=' || c == ':')) {
hasSep = true;
} else {
break;
}
}
valueStart++;
}
String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
setProperty(key, value, buffer.toString());
// reset buffer
buffer = new StringBuffer();
}
} /*
* 基于java.util.Properties.LineReader进行改造
*
* Read in a "logical line" from an InputStream/Reader, skip all comment and blank lines and filter out those leading whitespace characters ( , and ) from the beginning of a "natural line". Method
* returns the char length of the "logical line" and stores the line in "lineBuf".
*/
class LineReader {
public LineReader(InputStream inStream) {
this.inStream = inStream;
inByteBuf = new byte[8192];
} public LineReader(Reader reader) {
this.reader = reader;
inCharBuf = new char[8192];
} byte[] inByteBuf;
char[] inCharBuf;
char[] lineBuf = new char[1024];
int inLimit = 0;
int inOff = 0;
InputStream inStream;
Reader reader; int readLine() throws IOException {
int len = 0;
char c = 0; boolean skipWhiteSpace = true;
boolean isNewLine = true;
boolean appendedLineBegin = false;
boolean precedingBackslash = false;
boolean skipLF = false; while (true) {
if (inOff >= inLimit) {
inLimit = (inStream == null) ? reader.read(inCharBuf) : inStream.read(inByteBuf);
inOff = 0;
if (inLimit <= 0) {
if (len == 0) {
return -1;
}
return len;
}
}
if (inStream != null) {
// The line below is equivalent to calling a
// ISO8859-1 decoder.
c = (char) (0xff & inByteBuf[inOff++]);
} else {
c = inCharBuf[inOff++];
}
if (skipLF) {
skipLF = false;
if (c == '\n') {
continue;
}
}
if (skipWhiteSpace) {
if (c == ' ' || c == '\t' || c == '\f') {
continue;
}
if (!appendedLineBegin && (c == '\r' || c == '\n')) {
continue;
}
skipWhiteSpace = false;
appendedLineBegin = false;
}
if (isNewLine) {
isNewLine = false;
} if (c != '\n' && c != '\r') {
lineBuf[len++] = c;
if (len == lineBuf.length) {
int newLength = lineBuf.length * 2;
if (newLength < 0) {
newLength = Integer.MAX_VALUE;
}
char[] buf = new char[newLength];
System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
lineBuf = buf;
}
// flip the preceding backslash flag
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
} else {
// reached EOL
if (len == 0) {
isNewLine = true;
skipWhiteSpace = true;
len = 0;
continue;
}
if (inOff >= inLimit) {
inLimit = (inStream == null) ? reader.read(inCharBuf) : inStream.read(inByteBuf);
inOff = 0;
if (inLimit <= 0) {
return len;
}
}
if (precedingBackslash) {
len -= 1;
// skip the leading whitespace characters in following line
skipWhiteSpace = true;
appendedLineBegin = true;
precedingBackslash = false;
if (c == '\r') {
skipLF = true;
}
} else {
return len;
}
}
}
}
} /*
* Converts encoded \uxxxx to unicode chars and changes special saved chars to their original forms
*/
private String loadConvert(char[] in, int off, int len, char[] convtBuf) {
if (convtBuf.length < len) {
int newLen = len * 2;
if (newLen < 0) {
newLen = Integer.MAX_VALUE;
}
convtBuf = new char[newLen];
}
char aChar;
char[] out = convtBuf;
int outLen = 0;
int end = off + len; while (off < end) {
aChar = in[off++];
if (aChar == '\\') {
aChar = in[off++];
if (aChar == 'u') {
// Read the xxxx
int value = 0;
for (int i = 0; i < 4; i++) {
aChar = in[off++];
switch (aChar) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = (value << 4) + aChar - '0';
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
}
}
out[outLen++] = (char) value;
} else {
if (aChar == 't')
aChar = '\t';
else if (aChar == 'r')
aChar = '\r';
else if (aChar == 'n')
aChar = '\n';
else if (aChar == 'f')
aChar = '\f';
out[outLen++] = aChar;
}
} else {
out[outLen++] = (char) aChar;
}
}
return new String(out, 0, outLen);
} private void store0(BufferedWriter bw, boolean escUnicode) throws IOException {
synchronized (this) {
Iterator<Map.Entry<String, String>> kvIter = keyCommentMap.entrySet().iterator();
while (kvIter.hasNext()) {
Map.Entry<String, String> entry = kvIter.next();
String key = entry.getKey();
String val = getProperty(key);
String comment = entry.getValue();
key = saveConvert(key, true, escUnicode);
/*
* No need to escape embedded and trailing spaces for value, hence pass false to flag.
*/
val = saveConvert(val, false, escUnicode);
if (!comment.equals(BLANK))
writeComments(bw, comment);
bw.write(key + "=" + val);
bw.newLine();
}
}
bw.flush();
} private static void writeComments(BufferedWriter bw, String comments) throws IOException {
bw.write("#");
int len = comments.length();
int current = 0;
int last = 0;
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') {
bw.write(c);
} 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();
} /*
* 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' }; }
java读写properties配置文件不改变属性的顺序和注释的更多相关文章
- 【转】Java 读写Properties配置文件
[转]Java 读写Properties配置文件 1.Properties类与Properties配置文件 Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形 ...
- Java 读写Properties配置文件
Java 读写Properties配置文件 JAVA操作properties文件 1.Properties类与Properties配置文件 Properties类继承自Hashtable类并且实现了M ...
- java读写properties配置文件方法
1.Properties类 Properties类表示了一个持久的属性集.Properties可保存在流中或从流中加载,属性列表中的key和value必须是字符串. 虽然Properties类继承了j ...
- (转)Java 读写Properties配置文件
原文:http://www.cnblogs.com/xudong-bupt/p/3758136.html 1.Properties类与Properties配置文件 Properties类继承自Hash ...
- Java 读写Properties配置文件【转】
1.Properties类与Properties配置文件 Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存属性集.不过Properties有特殊的地 ...
- java获取properties配置文件中某个属性最简单方法
假如我想获取src目录下sysConfig.properties中的uploadpath属性的值 方法如下所示: private static final ResourceBundle bundle ...
- Java 读写Properties配置文件(转)
转自:http://www.cnblogs.com/xudong-bupt/p/3758136.html
- java 顺序 读写 Properties 配置文件
java 顺序 读写 Properties 配置文件 支持中文 不乱码 java 顺序 读写 Properties 配置文件 ,java默认提供的Properties API 继承hashmap ,不 ...
- 使用JAVA读写Properties属性文件
使用JAVA读写Properties属性文件 Properties属性文件在JAVA应用程序中是经常可以看得见的,也是特别重要的一类文件.它用来配置应用程序的一些信息,不过这些信息一般都是比较少的数 ...
随机推荐
- hdu 1568 (log取对数 / Fib数通项公式)
hdu 1568 (log取对数 / Fib数通项公式) 2007年到来了.经过2006年一年的修炼,数学神童zouyu终于把0到100000000的Fibonacci数列 (f[0]=0,f[1]= ...
- ThreadLocal的用法
阿里巴巴 java 开发手册中推荐的 ThreadLocal 的用法: public class DateUtil { public static final ThreadLocal<DateF ...
- Android-事件分发(ViewGroup)
http://blog.csdn.net/guolin_blog/article/details/9153747 http://blog.csdn.net/lmj623565791/article/d ...
- Java图片验证码乱码问题
有时部署到linux服务器上的web项目的图形验证码可能会出现乱码问题 这不是编码格式出错了,而是可能服务器上没有图形验证码中限定的那种字体 比如生成图形验证码的代码: Font font = new ...
- 使用ThinkPHP实现分页功能
前几篇(上传,缩略图,验证码,自动验证表单)文章介绍的功能实现都是基于ThinkPHP框架封装好的类进行实现的,所以这次自己写一个分页类在框架中使用. 首先在根目录建一个Tools文件夹,在Tools ...
- 【代码笔记】iOS-json文件的两种解析方式
一,工程图. 二,代码. #import "ViewController.h" #import "SBJson.h" @interface ViewContro ...
- 【代码笔记】iOS-请求去掉url中的空格
一,代码. - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, ...
- 企业如何选择合适的BI工具?
在没认清现状前,企业当然不能一言不合就上BI. BI不同于一般的企业管理软件,不能简单归类为类似用于提高管理的ERP和WMS,或用于提高企业效率的OA.BPM.BI的本质应该是通过展现数据,用于加强企 ...
- 网络基础 HTTP协议之http url简介
HTTP协议之http url简介 by:授客 QQ:1033553122 http url简介 http url通过http协议,用于定位网络资源,是一种特殊类型的URI(统一资源定位) http_ ...
- 如何在单元测试时隔离ORM
在项目中需要对DAL层进行单元测试,如果直接操作数据库,首先测试速度会大大下降,而且让单元测试直接使用外部依赖,很可能带来后续维护的不便,所以有必要对数据库隔离,然后单独测试DAL层.由于使用了ORM ...