ini 文件操作指南

  • 2019 年 10 月 17 日
  • 筆記

  今天总结一篇工具箱文章。

  ini 类型文件通常作为程序的初始化文件。不同于我们常见的配置文件通篇 key-value 的键值对形式,ini 文件在键值对的基础之上还有分类节点,比如我们常见的 Mysql 数据库的初始化配置文件 my.cnf或my.ini,其内容格式通常是如下这样的:

[client]  port = 3306  socket = /data/3306/mysql.sock    [mysqld]  user = mysql  port = 3306  socket = /data/3306/mysql.sock  basedir = /usr/local/mysql  datadir = /data/3306/data

  对于这种格式的文件的读取操作,Java中常用 Properties 类是不太好使的。当然,你也可以自己凭着高超的撸码水平去手写工具方法进行读写操作,但肯定还是比较费神的。实际上我们有第三方工具类库可供选择。此处博主分享的类库叫 org.dtools.javaini。整个工具包很轻便,能够支持基本的读写,格式校验等,官方教程很详细,花很少的时间就能上手。当然,结合项目的使用情况,自己还是需要自己进一步封装些方法以便更好的使用。前人种树,后人乘凉,博主也抛转引玉的写了几个:

package module.ini;    import com.alibaba.fastjson.JSONObject;  import org.dtools.ini.*;  import java.io.File;  import java.io.IOException;  import java.util.Iterator;  import java.util.Map;  import java.util.Set;    /**   * @date: 2019/10/16 19:11   * @author: chen   * @desc: org.dtools.javaini-v1.1.00.jar 工具包读写ini 文件工具类   */  public class IniUtil {        // ini 文件抽象表示      private static IniFile iniFile = null;      // 要操作的文件实例      private static File file = null;        // 操作 ini 文件的阅读器和书写器,通过它们来进行具体的读和写操作      private IniFileReader iniFileReader = null;      private IniFileWriter iniFileWriter = null;        /**       * @param filePath      文件路径       * @param caseSensitive 是否区分大小写 默认false       * @param validator     格式校验器       */      public IniUtil(String filePath, IniValidator validator, boolean caseSensitive) {          file = new File(filePath);          iniFile = new BasicIniFile(validator, caseSensitive);          try {              init();          } catch (IOException e) {              e.printStackTrace();          }      }        public IniUtil(String filePath) {          this(filePath, new IniValidator(), false);      }        public IniUtil(String filePath, IniValidator validator) {          this(filePath, validator, false);      }        public IniUtil(String filePath, boolean caseSensitive) {          this(filePath, new IniValidator(), caseSensitive);      }        /**       * 初始化 ini 文件的阅读器和书写器       */      private void init() throws IOException {          iniFileReader = new IniFileReader(iniFile, file);          iniFileWriter = new IniFileWriter(iniFile, file);          iniFileReader.read();      }        /**       * 读取 ini 文件转换数据为 Json对象       */      public JSONObject getIniFileInfo() {          Iterator<IniSection> sectionIterator = iniFile.iterator();          JSONObject jsonObject = new JSONObject();          while (sectionIterator.hasNext()) {              IniSection section = sectionIterator.next();              Iterator<IniItem> itemIterator = section.iterator();              JSONObject child = new JSONObject();              while (itemIterator.hasNext()) {                  IniItem item = itemIterator.next();                  child.put(item.getName(), item.getValue());              }              jsonObject.put(section.getName(), child);          }          return jsonObject;      }        /**       * 获取某个节点下的所有键值对       */      public JSONObject getIniBySection(String sectionName) throws Exception {          IniSection section = getSection(sectionName);          Iterator<IniItem> iterator = section.iterator();          JSONObject jsonObject = new JSONObject();          while (iterator.hasNext()) {              IniItem item = iterator.next();              jsonObject.put(item.getName(), item.getValue());          }          return jsonObject;      }          /**       * 获取某个节点下某个条目的值       */      public Object getItemByName(String sectionName, String itemName) throws Exception {          JSONObject iniBySection = this.getIniBySection(sectionName);          return iniBySection.get(itemName);      }          /**       * 新增或修改某节点条目       */      public void addOrUpdateItem(String sectionName, String itemName, String itemValue) throws Exception {          IniSection section = getSection(sectionName);          IniItem iniItem = section.addItem(itemName);          if (iniItem == null)              iniItem = section.getItem(itemName);          iniItem.setValue(itemValue);          iniFileWriter.write();      }          /**       * 新增或修改某节点条目 可添加注释       */      public void addOrUpdateItem(String sectionName, String itemName, String itemValue,String comment) throws Exception {          IniSection section = getSection(sectionName);          IniItem iniItem = section.addItem(itemName);          if (iniItem == null)              iniItem = section.getItem(itemName);          iniItem.setValue(itemValue);          iniItem.setPreComment(comment);  // 添加前置注释          iniFileWriter.write();      }          /**       * 新增或修改某节点条目       */      public void addOrUpdateItems(String sectionName, Map<String, Object> itemMap) throws Exception {          IniSection section = iniFile.getSection(sectionName);          Set<String> items = itemMap.keySet();          section.addItems(items.toArray(new String[0]));          iniFileWriter.write();      }        /**       * 新增节点       */      public void addSection(String sectionName) throws IOException {          if (iniFile.addSection(sectionName) == null)              return;          iniFileWriter.write();      }          /**       * 删除 某节点       */      public void removeSection(String sectionName) throws IOException {          if (iniFile.removeSection(sectionName) == null)              return;          iniFileWriter.write();      }        /**       * 删除 某条目       */      public void removeItem(String sectionName, String itemName) throws Exception {          IniSection section = getSection(sectionName);          section.removeItem(itemName);          iniFileWriter.write();      }          private IniSection getSection(String sectionName) throws Exception {          if (!iniFile.hasSection(sectionName)) {              throw new Exception("The ini file【" + file.getName() + "】 has no section named " + sectionName);          }          return iniFile.getSection(sectionName);      }    }

  作为一个比较简单的工具包,应对 ini 类型文件基本的增删改查足够了。如果你要深度使用,还有些问题需要你自己来解决——

    ① 工具包中对文件默认采用 ASCII 编码,所以对ASCII 码之外的字符比如中文字符就无法支持,如果读写过程中出现中文将会乱码;
    ② 对注释的支持也不足够。常见的注释为 #,但是作者却只给了以 ; 和 // 号开头的注释,,如果原文件有 # 类型的注释,读写操作后会被清理掉;

  由于类库中并没有提供设置编码或注释标注的方法,所以,比较好的办法就是自己下载源码,在源码上进行改动,然后自己打包到自己的项目中使用。以下是博主对代码的改动经验——

    首先,对于字符的问题,作者是在 IniFIleWriter 类中定义了一个表示字符的常量,去掉 final 修饰符,便于使用过程中动态修改编码:

                

 

   其次,对于注释,作者在 Commentable 接口中定义了表示注释开头的 ‘;’ 标识,由于是声明在接口中,默认就是static final 的,所以要改成动态设置的比较难办,改动会比较多,比较好的实践也是最简单粗暴的方式——直接改成通常的 # 注释符就可以了:

                

 

 

  最后,重新打成 jar 放入你的项目中或放入你的 maven 仓库。好了,工具方法,就长话短说了,祝使用愉快。

  如有什么问题或错漏,欢迎留言交流,不胜感激!