Match INI Section Blocks

I'm using regular expressions to try to match section blocks in an INI file. I'm using the recipe given in the book Regular Expressions Cookbook, but it doesn't seem to be working for me.

Here is the code I'm using:

final BufferedReader in = new BufferedReader(
    new FileReader(file));
String s;
String s2 = "";
while((s = in.readLine())!= null)
    s2 += s + System.getProperty("line.separator");
in.close();

final String regex = "^\\[[^\\]\r\n]+](?:\r?\n(?:[^\r\n].*)?)*";
final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
String sectionBlock = null;
final Matcher regexMatcher = pattern.matcher(s2);
if (regexMatcher.find()) {
    sectionBlock = regexMatcher.group();
}

Here are the contents of my input file:

[Section 2]
Key 2.0=Value 2.0
Key 2.2=Value 2.2
Key 2.1=Value 2.1

[Section 1]
Key 1.1=Value 1.1
Key 1.0=Value 1.0
Key 1.2=Value 1.2

[Section 0]
Key 0.1=Value 0.1
Key 0.2=Value 0.2
Key 0.0=Value 0.0

The problem is that sectionBlock ends up being equal to the entire contents of the file, rather than just the first section.

(I don't know whether it matters, but I'm doing this on Windows and the line separators in s2 are equal to "\r\n" (at least, that's what the IDEA debugger displays them as).)

What am I doing wrong here?

13.10.2009 16:52:15
I think the problem is Pattern.MULTILINE -- becuase you are using greedy quantifiers the regex will attempt to match as much as possible i.e. the entire contents of the file
heferav 13.10.2009 17:05:50
If I don't use Pattern.MULTILINE, I still get the whole file.
Paul Reiners 13.10.2009 17:41:05
2 ОТВЕТА
РЕШЕНИЕ

Try this regex instead:

(?ms)^\[[^]\r\n]+](?:(?!^\[[^]\r\n]+]).)*

or the Java String literal regex:

"(?ms)^\\[[^]\r\n]+](?:(?!^\\[[^]\r\n]+]).)*"

A (short) explanation:

(?ms)          // enable multi-line and dot-all matching
^              // the start of a line
\[             // match a '['
[^]\r\n]+      // match any character except '[', '\r' and '\n', one or more times
]              // match a ']'
(?:            // open non-capturing group 1
  (?!          //   start negative look-ahead
    ^          //     the start of a line
    \[         //     match a '['
    [^]\r\n]+  //     match any character except '[', '\r' and '\n', one or more times
    ]          //     match a ']'
  )            //   stop negative look-ahead
  .            //   any character (including line terminators)
)*             // close non-capturing group 1 and match it zero or more times

In plain English it would read as:

Match a '[' followed by one or more characters except '[', '\r' and '\n', followed by a ']' (let's call this match X). Then for every empty String in the text, first look ahead to see if you don't see a match X, if you don't, then match any character.

5
13.10.2009 17:56:08

You use the greedy quantifier * matching the longest possible string. Use the reluctant quantifier *? instead to get the shortest possible match.

0
13.10.2009 16:59:37
Do you mean like this?: "^\[[^\]\r\n]+](?:\r?\n(?:[^\r\n].*)?)*?" When I use that, it only returns me "[Section 2]", rather than the entire Section 2 block.
Paul Reiners 13.10.2009 17:39:29