zoukankan      html  css  js  c++  java
  • java读写properties配置文件不改变属性的顺序和注释

    先贴代码

      1 import java.io.BufferedWriter;
      2 import java.io.File;
      3 import java.io.FileInputStream;
      4 import java.io.IOException;
      5 import java.io.InputStream;
      6 import java.io.InputStreamReader;
      7 import java.io.OutputStream;
      8 import java.io.OutputStreamWriter;
      9 import java.io.Reader;
     10 import java.io.Writer;
     11 import java.util.Iterator;
     12 import java.util.LinkedHashMap;
     13 import java.util.Map;
     14 import java.util.Properties;
     15 import java.util.Set;
     16 
     17 /**
     18  * 扩展properties工具类
     19  * 
     20  * @author tangming
     21  * @date 2017-11-10
     22  */
     23 public class SafeProperties {
     24 
     25     /**
     26      * 内部属性表
     27      */
     28     private final Properties props;
     29 
     30     /**
     31      * 保存key与comment的映射, 同时利用这个映射来保证key的顺序。
     32      */
     33     private final LinkedHashMap<String, String> keyCommentMap = new LinkedHashMap<String, String>();
     34 
     35     private static final String BLANK = "";
     36 
     37     public SafeProperties() {
     38         super();
     39         props = new Properties();
     40     }
     41 
     42     public SafeProperties(Properties defaults) {
     43         super();
     44         props = new Properties(defaults);
     45     }
     46 
     47     /**
     48      * 设置一个属性,如果key已经存在,那么将其对应value值覆盖。
     49      * 
     50      * @param key
     51      * @param value
     52      * @return
     53      */
     54     public String setProperty(String key, String value) {
     55         return setProperty(key, value, BLANK);
     56     }
     57 
     58     /**
     59      * 设置一个属性,如果key已经存在,那么将其对应value值覆盖。
     60      * 
     61      * @param key 键
     62      * @param value 与键对应的值
     63      * @param comment 对键值对的说明
     64      * @return
     65      */
     66     public synchronized String setProperty(String key, String value, String comment) {
     67         Object oldValue = props.setProperty(key, value);
     68         if (BLANK.equals(comment)) {
     69             if (!keyCommentMap.containsKey(key)) {
     70                 keyCommentMap.put(key, comment);
     71             }
     72         } else {
     73             keyCommentMap.put(key, comment);
     74         }
     75         return (String) oldValue;
     76     }
     77 
     78     /**
     79      * 根据key获取属性表中相应的value。
     80      * 
     81      * @param key
     82      * @return
     83      */
     84     public String getProperty(String key) {
     85         return props.getProperty(key);
     86     }
     87 
     88     /**
     89      * 根据key获取属性表中相应的value。 如果没找到相应的value,返回defaultValue。
     90      * 
     91      * @param key
     92      * @param defaultValue
     93      * @return
     94      */
     95     public String getProperty(String key, String defaultValue) {
     96         return props.getProperty(key, defaultValue);
     97     }
     98 
     99     /**
    100      * 从一个字符流中读取属性到属性表中
    101      * 
    102      * @param reader
    103      * @throws IOException
    104      */
    105     public synchronized void load(Reader reader) throws IOException {
    106         load0(new LineReader(reader));
    107     }
    108 
    109     /**
    110      * 从一个字节流中读取属性到属性表中
    111      * 
    112      * @param inStream
    113      * @throws IOException
    114      */
    115     public synchronized void load(InputStream inStream) throws IOException {
    116         load0(new LineReader(inStream));
    117     }
    118 
    119     /**
    120      * 从一个字节流中读取属性到属性表中
    121      * 
    122      * @param inStream
    123      * @param charset
    124      * @throws IOException
    125      */
    126     public synchronized void load(InputStream inStream, String charset) throws IOException {
    127         InputStreamReader reader = new InputStreamReader(inStream, charset);
    128         load0(new LineReader(reader));
    129     }
    130 
    131     /**
    132      * 从一个文件中读取属性到属性表中
    133      * 
    134      * @param file 属性文件
    135      * @param charset 字符集
    136      * @throws IOException
    137      */
    138     public synchronized void load(File file, String charset) throws IOException {
    139         FileInputStream inputStream = new FileInputStream(file);
    140         InputStreamReader reader = new InputStreamReader(inputStream, charset);
    141         load0(new LineReader(reader));
    142     }
    143 
    144     /**
    145      * 从一个文件中读取属性到属性表中 默认字符集为utf-8
    146      * 
    147      * @param file 属性文件
    148      * @throws IOException
    149      */
    150     public synchronized void load(File file) throws IOException {
    151         FileInputStream inputStream = new FileInputStream(file);
    152         InputStreamReader reader = new InputStreamReader(inputStream, "utf-8");
    153         load0(new LineReader(reader));
    154     }
    155 
    156     /**
    157      * 将属性表中的属性写到字符流里面。
    158      * 
    159      * @param writer
    160      * @throws IOException
    161      */
    162     public void store(Writer writer) throws IOException {
    163         store0((writer instanceof BufferedWriter) ? (BufferedWriter) writer : new BufferedWriter(writer), false);
    164     }
    165 
    166     /**
    167      * 将属性表中的属性写到字节流里面。
    168      * 
    169      * @param out
    170      * @throws IOException
    171      */
    172     public void store(OutputStream out) throws IOException {
    173         store0(new BufferedWriter(new OutputStreamWriter(out, "utf-8")), true);
    174     }
    175 
    176     /**
    177      * 如果属性表中某个key对应的value值和参数value相同 那么返回true,否则返回false。
    178      * 
    179      * @param value
    180      * @return
    181      */
    182     public boolean containsValue(String value) {
    183         return props.containsValue(value);
    184     }
    185 
    186     /**
    187      * 如果属性表中存在参数key,返回true,否则返回false。
    188      * 
    189      * @param key
    190      * @return
    191      */
    192     public boolean containsKey(String key) {
    193         return props.containsKey(key);
    194     }
    195 
    196     /**
    197      * 获取属性表中键值对数量
    198      * 
    199      * @return
    200      */
    201     public int size() {
    202         return props.size();
    203     }
    204 
    205     /**
    206      * 检查属性表是否为空
    207      * 
    208      * @return
    209      */
    210     public boolean isEmpty() {
    211         return props.isEmpty();
    212     }
    213 
    214     /**
    215      * 清空属性表
    216      */
    217     public synchronized void clear() {
    218         props.clear();
    219         keyCommentMap.clear();
    220     }
    221 
    222     /**
    223      * 获取属性表中所有key的集合。
    224      * 
    225      * @return
    226      */
    227     public Set<String> propertyNames() {
    228         return props.stringPropertyNames();
    229     }
    230 
    231     public synchronized String toString() {
    232         StringBuffer buffer = new StringBuffer();
    233         Iterator<Map.Entry<String, String>> kvIter = keyCommentMap.entrySet().iterator();
    234         buffer.append("[");
    235         while (kvIter.hasNext()) {
    236             buffer.append("{");
    237             Map.Entry<String, String> entry = kvIter.next();
    238             String key = entry.getKey();
    239             String val = getProperty(key);
    240             String comment = entry.getValue();
    241             buffer.append("key=" + key + ",value=" + val + ",comment=" + comment);
    242             buffer.append("}");
    243         }
    244         buffer.append("]");
    245         return buffer.toString();
    246     }
    247 
    248     public boolean equals(Object o) {
    249         // 不考虑注释说明是否相同
    250         return props.equals(o);
    251     }
    252 
    253     public int hashCode() {
    254         return props.hashCode();
    255     }
    256 
    257     private void load0(LineReader lr) throws IOException {
    258         char[] convtBuf = new char[1024];
    259         int limit;
    260         int keyLen;
    261         int valueStart;
    262         char c;
    263         boolean hasSep;
    264         boolean precedingBackslash;
    265         StringBuffer buffer = new StringBuffer();
    266 
    267         while ((limit = lr.readLine()) >= 0) {
    268             c = 0;
    269             keyLen = 0;
    270             valueStart = limit;
    271             hasSep = false;
    272             // 获取注释
    273             c = lr.lineBuf[keyLen];
    274             if (c == '#' || c == '!') {
    275                 String comment = loadConvert(lr.lineBuf, 1, limit - 1, convtBuf);
    276                 if (buffer.length() > 0) {
    277                     buffer.append("
    ");
    278                 }
    279                 buffer.append(comment);
    280                 continue;
    281             }
    282             precedingBackslash = false;
    283             while (keyLen < limit) {
    284                 c = lr.lineBuf[keyLen];
    285                 // need check if escaped.
    286                 if ((c == '=' || c == ':') && !precedingBackslash) {
    287                     valueStart = keyLen + 1;
    288                     hasSep = true;
    289                     break;
    290                 } else if ((c == ' ' || c == '	' || c == 'f') && !precedingBackslash) {
    291                     valueStart = keyLen + 1;
    292                     break;
    293                 }
    294                 if (c == '\') {
    295                     precedingBackslash = !precedingBackslash;
    296                 } else {
    297                     precedingBackslash = false;
    298                 }
    299                 keyLen++;
    300             }
    301             while (valueStart < limit) {
    302                 c = lr.lineBuf[valueStart];
    303                 if (c != ' ' && c != '	' && c != 'f') {
    304                     if (!hasSep && (c == '=' || c == ':')) {
    305                         hasSep = true;
    306                     } else {
    307                         break;
    308                     }
    309                 }
    310                 valueStart++;
    311             }
    312             String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
    313             String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
    314             setProperty(key, value, buffer.toString());
    315             // reset buffer
    316             buffer = new StringBuffer();
    317         }
    318     }
    319 
    320     /*
    321      * 基于java.util.Properties.LineReader进行改造
    322      * 
    323      * 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
    324      * returns the char length of the "logical line" and stores the line in "lineBuf".
    325      */
    326     class LineReader {
    327         public LineReader(InputStream inStream) {
    328             this.inStream = inStream;
    329             inByteBuf = new byte[8192];
    330         }
    331 
    332         public LineReader(Reader reader) {
    333             this.reader = reader;
    334             inCharBuf = new char[8192];
    335         }
    336 
    337         byte[] inByteBuf;
    338         char[] inCharBuf;
    339         char[] lineBuf = new char[1024];
    340         int inLimit = 0;
    341         int inOff = 0;
    342         InputStream inStream;
    343         Reader reader;
    344 
    345         int readLine() throws IOException {
    346             int len = 0;
    347             char c = 0;
    348 
    349             boolean skipWhiteSpace = true;
    350             boolean isNewLine = true;
    351             boolean appendedLineBegin = false;
    352             boolean precedingBackslash = false;
    353             boolean skipLF = false;
    354 
    355             while (true) {
    356                 if (inOff >= inLimit) {
    357                     inLimit = (inStream == null) ? reader.read(inCharBuf) : inStream.read(inByteBuf);
    358                     inOff = 0;
    359                     if (inLimit <= 0) {
    360                         if (len == 0) {
    361                             return -1;
    362                         }
    363                         return len;
    364                     }
    365                 }
    366                 if (inStream != null) {
    367                     // The line below is equivalent to calling a
    368                     // ISO8859-1 decoder.
    369                     c = (char) (0xff & inByteBuf[inOff++]);
    370                 } else {
    371                     c = inCharBuf[inOff++];
    372                 }
    373                 if (skipLF) {
    374                     skipLF = false;
    375                     if (c == '
    ') {
    376                         continue;
    377                     }
    378                 }
    379                 if (skipWhiteSpace) {
    380                     if (c == ' ' || c == '	' || c == 'f') {
    381                         continue;
    382                     }
    383                     if (!appendedLineBegin && (c == '
    ' || c == '
    ')) {
    384                         continue;
    385                     }
    386                     skipWhiteSpace = false;
    387                     appendedLineBegin = false;
    388                 }
    389                 if (isNewLine) {
    390                     isNewLine = false;
    391                 }
    392 
    393                 if (c != '
    ' && c != '
    ') {
    394                     lineBuf[len++] = c;
    395                     if (len == lineBuf.length) {
    396                         int newLength = lineBuf.length * 2;
    397                         if (newLength < 0) {
    398                             newLength = Integer.MAX_VALUE;
    399                         }
    400                         char[] buf = new char[newLength];
    401                         System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
    402                         lineBuf = buf;
    403                     }
    404                     // flip the preceding backslash flag
    405                     if (c == '\') {
    406                         precedingBackslash = !precedingBackslash;
    407                     } else {
    408                         precedingBackslash = false;
    409                     }
    410                 } else {
    411                     // reached EOL
    412                     if (len == 0) {
    413                         isNewLine = true;
    414                         skipWhiteSpace = true;
    415                         len = 0;
    416                         continue;
    417                     }
    418                     if (inOff >= inLimit) {
    419                         inLimit = (inStream == null) ? reader.read(inCharBuf) : inStream.read(inByteBuf);
    420                         inOff = 0;
    421                         if (inLimit <= 0) {
    422                             return len;
    423                         }
    424                     }
    425                     if (precedingBackslash) {
    426                         len -= 1;
    427                         // skip the leading whitespace characters in following line
    428                         skipWhiteSpace = true;
    429                         appendedLineBegin = true;
    430                         precedingBackslash = false;
    431                         if (c == '
    ') {
    432                             skipLF = true;
    433                         }
    434                     } else {
    435                         return len;
    436                     }
    437                 }
    438             }
    439         }
    440     }
    441 
    442     /*
    443      * Converts encoded &#92;uxxxx to unicode chars and changes special saved chars to their original forms
    444      */
    445     private String loadConvert(char[] in, int off, int len, char[] convtBuf) {
    446         if (convtBuf.length < len) {
    447             int newLen = len * 2;
    448             if (newLen < 0) {
    449                 newLen = Integer.MAX_VALUE;
    450             }
    451             convtBuf = new char[newLen];
    452         }
    453         char aChar;
    454         char[] out = convtBuf;
    455         int outLen = 0;
    456         int end = off + len;
    457 
    458         while (off < end) {
    459             aChar = in[off++];
    460             if (aChar == '\') {
    461                 aChar = in[off++];
    462                 if (aChar == 'u') {
    463                     // Read the xxxx
    464                     int value = 0;
    465                     for (int i = 0; i < 4; i++) {
    466                         aChar = in[off++];
    467                         switch (aChar) {
    468                         case '0':
    469                         case '1':
    470                         case '2':
    471                         case '3':
    472                         case '4':
    473                         case '5':
    474                         case '6':
    475                         case '7':
    476                         case '8':
    477                         case '9':
    478                             value = (value << 4) + aChar - '0';
    479                             break;
    480                         case 'a':
    481                         case 'b':
    482                         case 'c':
    483                         case 'd':
    484                         case 'e':
    485                         case 'f':
    486                             value = (value << 4) + 10 + aChar - 'a';
    487                             break;
    488                         case 'A':
    489                         case 'B':
    490                         case 'C':
    491                         case 'D':
    492                         case 'E':
    493                         case 'F':
    494                             value = (value << 4) + 10 + aChar - 'A';
    495                             break;
    496                         default:
    497                             throw new IllegalArgumentException("Malformed \uxxxx encoding.");
    498                         }
    499                     }
    500                     out[outLen++] = (char) value;
    501                 } else {
    502                     if (aChar == 't')
    503                         aChar = '	';
    504                     else if (aChar == 'r')
    505                         aChar = '
    ';
    506                     else if (aChar == 'n')
    507                         aChar = '
    ';
    508                     else if (aChar == 'f')
    509                         aChar = 'f';
    510                     out[outLen++] = aChar;
    511                 }
    512             } else {
    513                 out[outLen++] = (char) aChar;
    514             }
    515         }
    516         return new String(out, 0, outLen);
    517     }
    518 
    519     private void store0(BufferedWriter bw, boolean escUnicode) throws IOException {
    520         synchronized (this) {
    521             Iterator<Map.Entry<String, String>> kvIter = keyCommentMap.entrySet().iterator();
    522             while (kvIter.hasNext()) {
    523                 Map.Entry<String, String> entry = kvIter.next();
    524                 String key = entry.getKey();
    525                 String val = getProperty(key);
    526                 String comment = entry.getValue();
    527                 key = saveConvert(key, true, escUnicode);
    528                 /*
    529                  * No need to escape embedded and trailing spaces for value, hence pass false to flag.
    530                  */
    531                 val = saveConvert(val, false, escUnicode);
    532                 if (!comment.equals(BLANK))
    533                     writeComments(bw, comment);
    534                 bw.write(key + "=" + val);
    535                 bw.newLine();
    536             }
    537         }
    538         bw.flush();
    539     }
    540 
    541     private static void writeComments(BufferedWriter bw, String comments) throws IOException {
    542         bw.write("#");
    543         int len = comments.length();
    544         int current = 0;
    545         int last = 0;
    546         while (current < len) {
    547             char c = comments.charAt(current);
    548             if (c > 'u00ff' || c == '
    ' || c == '
    ') {
    549                 if (last != current)
    550                     bw.write(comments.substring(last, current));
    551                 if (c > 'u00ff') {
    552                     bw.write(c);
    553                 } else {
    554                     bw.newLine();
    555                     if (c == '
    ' && current != len - 1 && comments.charAt(current + 1) == '
    ') {
    556                         current++;
    557                     }
    558                     if (current == len - 1
    559                             || (comments.charAt(current + 1) != '#' && comments.charAt(current + 1) != '!'))
    560                         bw.write("#");
    561                 }
    562                 last = current + 1;
    563             }
    564             current++;
    565         }
    566         if (last != current)
    567             bw.write(comments.substring(last, current));
    568         bw.newLine();
    569     }
    570 
    571     /*
    572      * Converts unicodes to encoded &#92;uxxxx and escapes special characters with a preceding slash
    573      */
    574     private String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode) {
    575         int len = theString.length();
    576         int bufLen = len * 2;
    577         if (bufLen < 0) {
    578             bufLen = Integer.MAX_VALUE;
    579         }
    580         StringBuffer outBuffer = new StringBuffer(bufLen);
    581 
    582         for (int x = 0; x < len; x++) {
    583             char aChar = theString.charAt(x);
    584             // Handle common case first, selecting largest block that
    585             // avoids the specials below
    586             if ((aChar > 61) && (aChar < 127)) {
    587                 if (aChar == '\') {
    588                     outBuffer.append('\');
    589                     outBuffer.append('\');
    590                     continue;
    591                 }
    592                 outBuffer.append(aChar);
    593                 continue;
    594             }
    595             switch (aChar) {
    596             case ' ':
    597                 if (x == 0 || escapeSpace)
    598                     outBuffer.append('\');
    599                 outBuffer.append(' ');
    600                 break;
    601             case '	':
    602                 outBuffer.append('\');
    603                 outBuffer.append('t');
    604                 break;
    605             case '
    ':
    606                 outBuffer.append('\');
    607                 outBuffer.append('n');
    608                 break;
    609             case '
    ':
    610                 outBuffer.append('\');
    611                 outBuffer.append('r');
    612                 break;
    613             case 'f':
    614                 outBuffer.append('\');
    615                 outBuffer.append('f');
    616                 break;
    617             case '=': // Fall through
    618             case ':': // Fall through
    619             case '#': // Fall through
    620             case '!':
    621                 outBuffer.append('\');
    622                 outBuffer.append(aChar);
    623                 break;
    624             default:
    625                 if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode) {
    626                     outBuffer.append('\');
    627                     outBuffer.append('u');
    628                     outBuffer.append(toHex((aChar >> 12) & 0xF));
    629                     outBuffer.append(toHex((aChar >> 8) & 0xF));
    630                     outBuffer.append(toHex((aChar >> 4) & 0xF));
    631                     outBuffer.append(toHex(aChar & 0xF));
    632                 } else {
    633                     outBuffer.append(aChar);
    634                 }
    635             }
    636         }
    637         return outBuffer.toString();
    638     }
    639 
    640     /**
    641      * Convert a nibble to a hex character
    642      * 
    643      * @param nibble the nibble to convert.
    644      */
    645     private static char toHex(int nibble) {
    646         return hexDigit[(nibble & 0xF)];
    647     }
    648 
    649     /** A table of hex digits */
    650     private static final char[] hexDigit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
    651             'F' };
    652 
    653 }
  • 相关阅读:
    MySQL索引类型
    Spring+Quartz框架实现定时任务(集群,分布式)
    搭建Nginx+Java环境(转)
    windows环境下将csv文件导入mysql
    哈利波特折扣
    第二阶段个人总结06
    第二阶段个人总结05
    第二阶段个人总结04
    第二阶段个人总结03
    学习进度条——第13周
  • 原文地址:https://www.cnblogs.com/wangzhisdu/p/7815549.html
Copyright © 2011-2022 走看看