Java regex lookbehind is not working as js regex lookbehind

Viewed 120

I had this objective :

Given string : "Part1-part2-part3-part4-part5"
Split it on second occurrence of '-',
So I expect an array [ "Part1-part2", "part3-part4-part5" ]

What I did :

"Part1-part2-part3-part4-part5".split("(?<=^\\w+-\\w+)-"

But result on jdk 8 :
It does not find match of 2nd '-', and returns the entire string.
Evidence : https://ideone.com/myWppm

But when I tried on online regex processing websites and node.js (or chrome) [Believe me modern js supports lookbehind] the result is as expected.
Evidence : https://ideone.com/ttQWNr

Hack that I'm using right now (does not qualify to be a solution) :

Using lookahead instead of lookbehind, 3rd occurrence of '-' from the end
"Part1-part2-part3-part4-part5".split("-(?=\\w+-\\w+-\\w+$)");

3 Answers

Java does support finite lookbehind using a quantifier for example {0,100}

To match any char except a hyphen, you could use [^-] which is a negated character class.

If you want to exclude matching newlines, you could extend it to [^-\\r\\n]

You might use:

(?<=^[^-]{0,100}-[^-]{0,100})-

In parts

  • (?<= Positive lookbehind, assert what is on the left is
    • ^ Start of string
    • [^-]{0,100}- Match 0 - 100 times any char except -, then match the first -
    • [^-]{0,100} Match 0 - 100 times any char except-`
  • ) Close lookbehind
  • - Match the second - to split on

Regex demo | Java demo

For example

System.out.println(
    Arrays.toString(
        "Part1-part2-part3-part4-part5".split("(?<=^[^-]{0,100}-[^-]{0,100})-")
    )
);

Output

[Part1-part2, part3-part4-part5]

Instead of using split(), use matching:

String input = "Part1-part2-part3-part4-part5";
String regex = "(\\w+-\\w+)-(.*)"
String[] result; // just to simulate result of split()
Matcher m = Pattern.compile(regex).matcher(input);
if (m.matches()) {
    result = new String[] { m.group(1), m.group(2) };
} else {
    result = new String[] { input };
}

Sure it's a bit more code, but you can easily enhance the regex to perform more validation, e.g. no special characters other than - (and _ apparently), even for the text after the second dash.

Variable width lookbehinds are not supported by Java. Assuming your input string would always have five hyphen separated terms, we could instead phrase the splitting logic by checking that there be two hyphens in front before spltting:

String input = "Part1-part2-part3-part4-part5";
String[] parts = input.split("-(?=[^-]+-[^-]+-[^-]+$)");
System.out.println(Arrays.toString(parts));

This prints:

[Part1-part2, part3-part4-part5]
Related