views:

99

answers:

2

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?

A: 

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

tangens
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
+2  A: 

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.

Bart Kiers