知識庫 / Java IO RSS 訂閱

如何在 Java 中解析 INI 文件

Jackson,Java IO
HongKong
6
09:50 PM · Dec 05 ,2025

1. 概述

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

在本教程中,我們將探討一些可以幫助我們處理 INI 文件的庫。我們還將學習如何使用這些庫將數據填充到 POJO 中。

2. 創建一個示例 INI 文件

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

; for 16-bit app support
[fonts]
letter=bold
text-size=28

[background]
color=white

[RequestResult]
RequestCode=1

[ResponseResult]
ResultCode=0

該文件包含四個部分,使用了混合的命名方式,包括小寫、kebab-case和大駝峯命名法。它包含字符串或數字值。

3. 使用 ini4j 解析 INI 文件

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);

這個對象現在包含 sections 和 keys。

3.3. 讀取節區鍵

可以使用 Ini 類中的 <em get()</em> 函數,從 INI 文件中讀取節區鍵。

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

3.4. 轉換為 Map

讓我們看看如何輕鬆地將整個 INI 文件轉換為 `<em 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 對象的鍵值對,本質上是 <em style="font-style: italic;">String</em><em style="font-style: italic;">Map&lt;String, String&gt;</em>》。Ini對象的內部表示實際上就是一個Map,因此可以通過使用stream()toMap()收集器輕鬆轉換為一個普通的Map`。

現在,我們可以使用 get() 方法讀取這個 map 中的 section:

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

這個 Ini 類非常容易直接使用,雖然轉換為 Map 可能並不必要,但稍後我們會找到它的用途。

然而,ini4j 是一箇舊庫,目前來看似乎沒有得到很好的維護。我們考慮另一個選項。

4. 使用 Apache Commons 解析 INI 文件

Apache Commons 提供了一個更強大的工具來處理 INI 文件。該工具能夠對整個文件進行建模,支持讀寫操作,儘管我們將重點關注其解析功能。

4.1. 安裝 Commons 配置

讓我們首先在我們的 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. 讀取節標題

<em>INIConfiguration</em> 類包含一個 <em>getSection()</em> 函數用於讀取節標題,以及在返回的對象的 <em>getProperty()</em> 函數用於讀取節標題中的鍵:

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);
}

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

我們可以使用Iterator,它提供所有部分的鍵,並使用getProperty()與每個鍵值對進行操作。

雖然Map在這裏更難生成,但更簡單的數據結構允許我們隱藏INI文件解析與其他系統部分。

5. 將 INI 文件轉換為 POJO

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

5.1. 導入 Jackson

我們需要將 Jackson 添加到我們的 `pom.xml</em/> 中:

<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

<em font="fonts"</em> 部分在我們的示例文件中使用 kebab-case 格式來定義其屬性。 讓我們定義一個類來表示該部分:

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

    // getters and setters
}

我們使用了 JsonNaming 註解來描述屬性中使用的案例。

同樣,RequestResult 部分的屬性使用了大駝峯命名法:

@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

現在我們既有使用任何一個庫讀取 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

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

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

發佈 評論

Some HTML is okay.