首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 實現 INI 風格配置文件讀寫:輕量級鍵值對持久化工具類

     編輯:Java 程式設計入門

在不想引入 Apache Commons Configuration、SnakeYAML 或 Jackson 等重型庫的項目中,手寫一個支援「區段/鍵/值」結構、帶註解保留、線程安全的 INI 解析器是極佳的工程練習,也能滿足中小型專案的配置管理需求。本文將重構原始代碼,修復其 **NPE 捕獲控制流、資源洩漏、併發不安全、註解丟失、編碼假設** 等嚴重缺陷,打造一個生產可用的 IniFile 工具類。

原始代碼的四大硬傷

  1. 濫用 NullPointerException 控制流程: 以 `try-catch (NullPointerException)` 判斷檔案讀取結束,**極其反模式**,效能極差且掩蓋真實 Bug。
  2. 資源管理缺陷: 手動 `close()` 且無 `try-with-resources`,異常路徑導致檔案句柄洩漏。
  3. 非線程安全: `Read/Write/Del` 操作無同步,多線程環境下數據競爭、檔案損壞風險極高。
  4. 格式破壞: 寫入時**拋棄原始註解、空行、排序**,且強制所有行尾加分號 `;`,不符合標準 INI 風格 (通常 `key=value` 無分號)。

重構目標與設計原則

  • ✅ **標準格式相容**:支援 `[section]`, `key=value`, `;註解`, `#註解`, 內聯註解。
  • ✅ **完美保留**:讀取→修改→寫入,**最大程度保留原始格式、縮進、註解、空行**。
  • ✅ **線程安全**:單檔案實例級鎖,支援多線程並發讀寫。
  • ✅ **資源安全**:全面採用 `try-with-resources`,指定 UTF-8 編碼。
  • ✅ **API 直觀**:`get(section, key)`, `set(section, key, val)`, `remove(section, key)`, `save()`。

核心數據結構:有序映射樹

// 節點類型區分
enum NodeType { SECTION, KEY_VALUE, COMMENT, BLANK_LINE }

// 統一節點結構,保留原始行文本與解析後數據
class IniNode {
    NodeType type;
    String rawLine;      // 原始行 (含縮進、註解)
    String section;      // 區段名 (若為 SECTION 或 KEY_VALUE)
    String key;          // 鍵名
    String value;        // 鍵值
    String comment;      // 內聯/整行註解內容
    int lineNumber;      // 原始行號
}

// 主存儲:有序列表保證寫入順序 = 讀取順序
private final List nodes = new ArrayList<>();
// 輔助索引:快速定位 (區段+鍵) -> 節點列表索引
private final Map> index = new LinkedHashMap<>();

關鍵實現:解析器 (單次遍歷,狀態機)

public void load() throws IOException {
    nodes.clear(); index.clear();
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
        
        String currentSection = ""; // 預設無區段
        String line;
        int lineNum = 0;
        while ((line = reader.readLine()) != null) {
            lineNum++;
            String trimmed = line.stripLeading(); // 保留尾部空格? 通常strip()更安全
            IniNode node = new IniNode();
            node.lineNumber = lineNum;
            node.rawLine = line; // 完整保留
            
            if (trimmed.isEmpty()) {
                node.type = NodeType.BLANK_LINE;
            } else if (trimmed.startsWith(";") || trimmed.startsWith("#")) {
                node.type = NodeType.COMMENT;
                node.comment = trimmed.substring(1).stripLeading();
            } else if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
                node.type = NodeType.SECTION;
                currentSection = node.section = trimmed.substring(1, trimmed.length()-1).strip();
            } else {
                // 尋找第一個 '=' 分割 key/value,支援內聯註解
                int eqIdx = findFirstUnquotedEquals(trimmed); // 需處理值中含 '=' 情況
                if (eqIdx > 0) {
                    node.type = NodeType.KEY_VALUE;
                    node.section = currentSection;
                    node.key = trimmed.substring(0, eqIdx).stripTrailing();
                    String valPart = trimmed.substring(eqIdx+1);
                    // 分離值與內聯註解
                    int commentIdx = findFirstUnquotedComment(valPart);
                    if (commentIdx >= 0) {
                        node.value = valPart.substring(0, commentIdx).stripTrailing();
                        node.comment = valPart.substring(commentIdx+1).stripLeading();
                    } else {
                        node.value = valPart.stripTrailing();
                    }
                } else {
                    // 格式錯誤行視為註解保留
                    node.type = NodeType.COMMENT;
                    node.comment = "[格式錯誤] " + trimmed;
                }
            }
            nodes.add(node);
            if (node.type == NodeType.KEY_VALUE) {
                index.computeIfAbsent(node.section + "/" + node.key, k -> new ArrayList<>()).add(nodes.size()-1);
            }
        }
    }
}

// 輔助:尋找第一個未在引號內的 '=' (簡易實現)
private int findFirstUnquotedEquals(String s) {
    boolean inSingle = false, inDouble = false;
    for (int i=0; i
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved