Tuesday, June 9, 2009

Match until pattern!

So, given some HL7v2 input like this:
String input =
"MSH|^~\\&|||||||ORM^O01|\r" +
"PID|||\r" +
"PV1|||\r" +
"ORC|NW||||Registered\r" +
"OBR||ASDF00001||||||||||||||||||ASDF01000\r" +
"ZDS|1.2.752.24.2.5234.234.123.14.23\r" +
"ORC|NW||||Registered\r" +
"OBR||ASDF01000||||||||||||||||||ASDF01000\r" +
"ZDS|1.2.752.24.2.5234.234.123.14.23\r";
I needed to split this into two separate messages, both containing the header (MSH...PV1|||) and each containing one of the ORC/OBR/ZDS segment groups.

I'm currently reading the (so far) excellent book "Mastering Regular Expressions" by Friedl so why not use regular expressions for this, I thought!

First try (these are from memory, not sure I get them right):
Pattern getHeader = Pattern.compile("(MSH\\|[^ORC]*)",Pattern.MULTILINE | Pattern.DOTALL)

Well, this won't work since [^] is a character class and matches any of the O, R or C characters. Next try:
(MSH\\|.*ORC)
This pattern won't work because by default regexp matches greedily so MSH...ORC|...ORC|... would be matched.

After flipping around in the book for a while I discovered lazy quantifiers. But the Friedl had an example on the same page where this problem could be solved by negative lookahead.
The final result:

(^MSH(?:(?!^ORC).)*)(.*)
This pattern matches:
  • Beginning of line
  • MSH
  • Any character as long as the next characters aren't beginning of line and ORC
The matched part is stored in the first group and the rest of the message is stored in the second.
Similarly I can get the ORC/OBR/ZDS segments with the pattern:
^(ORC(?:(?!^ORC).)*)
which matches:
  • Beginning of line
  • any character as long as the next ones aren't beginning of line and ORC
Note: Pattern.MULTILINE allows me to match beginning/end of line even when it appears in the middle of the string. Pattern.DOTALL allows . to match line delimiters.

Final source:
public List filterMessageString(String input) {
List result = new ArrayList();

// first get the header
Pattern getHeader = Pattern.compile("(^MSH(?:(?!^ORC).)*)(.*)", Pattern.MULTILINE | Pattern.DOTALL);
Matcher m = getHeader.matcher(input);
m.find();
String header = m.group(1);
if(logger.isDebugEnabled())
logger.debug("header: " + header);

// get the orc-obr-zds groups
String ORCGroups = m.group(2);

Pattern getNextOBR = Pattern.compile("^(ORC(?:(?!^ORC).)*)", Pattern.MULTILINE | Pattern.DOTALL);
m = getNextOBR.matcher(ORCGroups);
while(m.find()) {
if(logger.isDebugEnabled())
logger.debug("Found segment: " + m.group(1));
result.add(header + m.group(1));
}

return result;
}

No comments:

Post a Comment