如何在Java中解析INI文件

Jackson,Java IO
Remote
0
09:17 PM · Nov 30 ,2025

1. 概述

INI 文件是 Windows 或 MS-DOS 的初始化或配置文件。它們包含純文本內容,該內容由鍵值對組成,並按部分進行組織。雖然我們可能更喜歡使用 Java 的原生 .properties 文件或其他格式來配置我們的應用程序,但在某些情況下,我們可能需要從現有 INI 文件中消費數據。

在本教程中,我們將探討一些可以幫助我們使用的庫。我們還將研究如何使用這些庫將數據填充到 POJO 中。

2. 創建一個示例 INI 文件

讓我們從一個示例 INI 文件,sample.ini

; 用於 16 位應用程序支持
[fonts]
letter=bold
text-size=28

[background]
color=white

[RequestResult]
RequestCode=1

[ResponseResult]
ResultCode=0

此文件包含四個部分,使用混合大小寫(小寫、kebab-case 和大駝峯命名法)進行命名。 它包含字符串或數字值。

3. 解析 INI 文件使用 ini4j

ini4j 是一個輕量級庫,用於從 INI 文件中讀取配置。它自 2015 年以來一直未更新。

3.1. 安裝 ini4j

為了可以使用 ini4j 庫,首先,我們需要在我們的 pom.xml 中添加它的依賴:

<dependency>
    <groupId>org.ini4j</groupId>
    <artifactId>ini4j</artifactId>
    <version>0.5.4</version>
</dependency>

3.2. 在 ini4j 中打開 INI 文件

我們可以通過構造一個 Ini 對象來在 ini4j 中打開一個 INI 文件:

File fileToParse = new File("sample.ini");
Ini ini = new Ini(fileToParse);

這個對象現在包含節和鍵。

3.3. 讀取節中的鍵

我們可以使用 get() 函數從 INI 文件中的節中讀取一個鍵:

assertThat(ini.get("fonts", "letter"))
  .isEqualTo("bold");

3.4. 轉換為 Map

讓我們看看將整個 INI 文件轉換為 Map<String, Map<String, String>> 的有多容易,這是一種 Java 原生數據結構,表示 INI 文件的層次結構:

public static Map<String, Map<String, String>> parseIniFile(File fileToParse)
  throws IOException {
    Ini ini = new Ini(fileToParse);
    return ini.entrySet().stream()
      .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
}

在這裏,entrySetIni 對象本質上是一個 Map<String, String> 的鍵值對。 Ini 對象的內部表示實際上是一個 Map,因此可以使用 stream() 和 toMap() 收集器輕鬆轉換為一個普通的 Map。

現在我們可以使用 get() 函數從這個映射中讀取節:

assertThat(result.get("fonts").get("letter"))
  .isEqualTo("bold");

Ini 類易於使用,無需轉換為 Map,儘管稍後我們會找到它的用途。

但是,ini4j 是一箇舊庫,並且看起來沒有得到積極維護。讓我們考慮另一個選項。

4. 解析 INI 文件使用 Apache Commons

Apache Commons 具有用於處理 INI 文件的更高級工具。它能夠對整個文件進行建模,用於讀取和寫入,儘管我們將僅關注其解析功能。

4.1. 安裝 Commons Configuration

首先,我們在 依賴項中添加所需的 pom.xml


<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-configuration2</artifactId>
    <version>2.8.0</version>
</dependency>

版本 2.8.0 於 2022 年更新,比 ini4j 更新。

4.2. 打開 INI 文件

我們可以通過聲明一個 INIConfiguration 對象並將其傳遞一個 Reader 來打開 INI 文件:


INIConfiguration iniConfiguration = new INIConfiguration();
try (FileReader fileReader = new FileReader(fileToParse)) {
    iniConfiguration.read(fileReader);
}

我們使用了 try-with-resources 模式來打開一個 FileReader,然後要求 INIConfiguration 對象使用 read 函數讀取它。

4.3. 讀取一個 Section Key

INIConfiguration 類具有一個 getSection() 函數來讀取一個 section,以及一個 getProperty() 函數用於讀取一個 key:


String value = iniConfiguration.getSection("fonts")
  .getProperty("letter")
  .toString();
assertThat(value)
  .isEqualTo("bold");

我們應該注意的是,getProperty() 返回 Object 而不是 String,因此需要轉換為 String

4.4. 轉換為 Map

我們可以像之前一樣將 INIConfiguration 轉換為 Map。這比使用 ini4j 更加複雜:


Map<String, Map<String, String>> iniFileContents = new HashMap<>();

for (String section : iniConfiguration.getSections()) {
    Map<String, String> subSectionMap = new HashMap<>();
    SubnodeConfiguration confSection = iniConfiguration.getSection(section);
    Iterator<String> keyIterator = confSection.getKeys();
    while (keyIterator.hasNext()) {
        String key = keyIterator.next();
        String value = confSection.getProperty(key).toString();
        subSectionMap.put(key, value);
    }
    iniFileContents.put(section, subSectionMap);
}

為了獲取所有 section,我們需要使用 getSections() 來找到它們的名稱。然後 getSection() 可以為我們提供每個 section。

然後我們可以使用 Iterator,該 iterator 提供所有 section 的 key,並使用 getProperty() 與每個 key 組合來獲取 key-value 對。

雖然一個 Map 更難生成,但更平坦的數據結構的好處是我們可以隱藏 INI 文件解析從其他系統部分中。或者,我們可以將配置轉換為 POJOs。

5. 將 INI 文件轉換為 POJO

我們可以使用 Jackson 將我們的 Map 結構轉換為 POJO。我們可以使用反序列化註解來裝飾我們的 POJO,以幫助 Jackson 理解原始 INI 文件中各種命名約定。POJO 比我們之前見過的任何數據結構都更容易使用。

5.1. 導入 Jackson

我們需要將 Jackson 添加到我們的 pom.xml:


<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.17.2</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.17.2</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.2</version>
</dependency>

5.2. 定義一些 POJO

我們的樣本文件中 fonts 部分使用 kebab-case 格式存儲其屬性。讓我們定義一個類來表示該部分:


@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class)
public static class Fonts {
    private String letter;
    private int textSize;

    // getters and setters
}

在這裏,我們使用了 JsonNaming 註解來描述屬性使用的格式。

同樣,RequestResult 部分的屬性使用 upper-camel-case:


@JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class)
public static class RequestResult {
    private int requestCode;

    // getters and setters
}

部分名稱本身是各種格式,因此我們可以聲明每個部分在我們的父對象中,使用 JsonProperty 註解來顯示與默認小駝峯命名不匹配的偏差:


public class MyConfiguration {
    private Fonts fonts;
    private Background background;

    @JsonProperty("RequestResult")
    private RequestResult requestResult;

    @JsonProperty("ResponseResult")
    private ResponseResult responseResult;

    // getters and setters
}

5.3. 從 Map 轉換為 POJO

現在,我們有了使用 Jackson 的庫之一讀取 INI 文件作為 Map 的能力,以及將文件的內容建模為 POJO 的能力,我們可以使用 Jackson 的 ObjectMapper 來執行轉換:


ObjectMapper objectMapper = new ObjectMapper();
Map<String, Map<String, String>> iniKeys = parseIniFile(TEST_FILE);
MyConfiguration config = objectMapper.convertValue(iniKeys, MyConfiguration.class);

讓我們檢查整個文件是否已正確加載:


assertThat(config.getFonts().getLetter()).isEqualTo("bold");
assertThat(config.getFonts().getTextSize()).isEqualTo(28);
assertThat(config.getBackground().getColor()).isEqualTo("white");
assertThat(config.getRequestResult().getRequestCode()).isEqualTo(1);
assertThat(config.getResponseResult().getResultCode()).isZero();

我們應該注意到,數字屬性,如 textSizerequestCode,已加載到我們的 POJO 中作為數字。

6. 庫和方法的比較ini4j庫非常簡單易用,本質上是一個類似於Map結構的簡單庫。但是,這仍然是一個沒有定期更新的舊庫。

Apache Commons解決方案功能更全面,並且有定期更新,但使用起來需要稍微多一些工作。

7. 結論

在本文中,我們學習瞭如何使用一些開源庫讀取 INI 文件。我們學習瞭如何讀取單個鍵以及如何迭代整個文件以生成一個 Map

然後我們學習瞭如何將 Map 轉換為 POJO,使用 Jackson。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.