java
57 lines · 10 steps
Parsing INI files in Java
A line-oriented parser that reads an INI file into nested maps of sections and key-value pairs.
Explained by
highlit
1public final class IniParser {
2
3 private static final Pattern SECTION = Pattern.compile("\\[(.+?)\\]");
4
5 public Map<String, Map<String, String>> parse(Path file) throws IOException {
6 Map<String, Map<String, String>> sections = new LinkedHashMap<>();
7 Map<String, String> current = sections.computeIfAbsent("", k -> new LinkedHashMap<>());
8
9 try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
10 String raw;
11 int lineNo = 0;
12 while ((raw = reader.readLine()) != null) {
13 lineNo++;
14 String line = stripComment(raw).trim();
15 if (line.isEmpty()) {
16 continue;
17 }
18
19 Matcher section = SECTION.matcher(line);
20 if (section.matches()) {
21 String name = section.group(1).trim();
22 current = sections.computeIfAbsent(name, k -> new LinkedHashMap<>());
23 continue;
24 }
25
26 int eq = line.indexOf('=');
27 if (eq < 0) {
28 throw new IllegalArgumentException("Malformed entry at line " + lineNo + ": " + raw);
29 }
30
31 String key = line.substring(0, eq).trim();
32 String value = unquote(line.substring(eq + 1).trim());
33 current.put(key, value);
34 }
35 }
36
37 sections.get("").entrySet().removeIf(e -> false);
38 if (sections.get("").isEmpty()) {
39 sections.remove("");
40 }
41 return sections;
42 }
43
44 private static String stripComment(String line) {
45 int hash = line.indexOf('#');
46 int semi = line.indexOf(';');
47 int cut = Math.min(hash < 0 ? line.length() : hash, semi < 0 ? line.length() : semi);
48 return line.substring(0, cut);
49 }
50
51 private static String unquote(String value) {
52 if (value.length() >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
53 return value.substring(1, value.length() - 1);
54 }
55 return value;
56 }
57}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Tracking a mutable current-section reference turns a flat line loop into a stateful parser.
- 2LinkedHashMap preserves insertion order, so sections and keys come back as they appeared in the file.
- 3Stripping comments and unquoting before parsing keeps the core logic focused on structure, not cleanup.
Related explainers
ruby
class UserAgentParser BROWSERS = [ [/Edg\/([\d.]+)/, "Edge"], [/OPR\/([\d.]+)/, "Opera"],
Parsing user-agent strings in Ruby
regex
pattern-matching
lookup-tables
Intermediate
8 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
java
package com.acme.billing.config; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties;
Feature-flagged beans with Spring @ConditionalOnProperty
feature-flags
conditional-beans
strategy-pattern
Intermediate
5 steps
rust
use chrono::{Duration, NaiveDate}; #[derive(Debug)] pub struct DateRange {
Parsing and iterating date ranges in Rust
error-handling
iterators
parsing
Intermediate
7 steps
java
public static Map<String, String> parseCookieHeader(String header) { Map<String, String> cookies = new LinkedHashMap<>(); if (header == null || header.isBlank()) { return cookies;
Parsing an HTTP Cookie header in Java
string-parsing
http
url-decoding
Intermediate
6 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/parsing-ini-files-in-java-explained-java-6a2b/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.