如何在Java中分割令牌进行复杂的负面观察?

时间:2014-10-24 22:14:59

标签: java regex tokenize regex-negation

我有许多EDIFACT格式的行需要在+上进行标记化。但是,根据EDIFACT规范,可以使用?转义字符。例如:????++?::?+是字段的一部分,因此不应被视为分隔符。

我使用负面的lookbehind来处理+之后的?

delimiter = "\\+";
String[] tokens = data.split("(?<!\\?)" + delimiter);

这会分开

a+b+c加入abc

a?+b+c加入a?+bc

但是,当涉及??转义序列时,它会失败:

a??+b+c会产生2个代币:a??+bc

而它确实应该是3个令牌:a?bc

另一方面:a???+b+c应该产生两个令牌:a???+bc

有没有办法用负面的后视来实现这个目标?

如果你愿意的话,这是一个可以玩的可运行的测试。

import java.util.Arrays;

public class Main {
   public static void main(String[] args) {
      assertTokens("a+b+c", "a", "b", "c");
      assertTokens("a?+b+c", "a?+b", "c");
      assertTokens("a??+b+c", "a??", "b", "c");
      assertTokens("a???+b+c", "a???+b", "c");
   }

   private static void assertTokens(String data, String... expectedTokens) {
      String delimiter = "\\+";
      String[] tokens = data.split("(?<!\\?)" + delimiter);

      if(!Arrays.deepEquals(tokens, expectedTokens)) {
         throw new IllegalStateException("Not equals for " + data);
      }
   }

}

2 个答案:

答案 0 :(得分:4)

使用匹配更容易进行标记化,而不是拆分。在你的情况下,对于拆分工作,你必须使用java不支持的可变长度lookbehind。

尝试以下正则表达式:

(?:[^+:?]++|\?.)+

DEMO

(我使用占有量词(++)纯粹作为优化来避免无用的回溯)


如果您想匹配空标记(a++b让步,a,空字符串和b),则正则表达式会变得更复杂:

(?:[^+:?\r\n]++|\?.)+|(?<=[+:]|^)(?=[+:]|$)

DEMO

这意味着

  • 与上述相同(我刚刚将\r\n添加到论坛中,因此换行符不匹配)
  • 或者是一个空字符串:
    • 前面有一个标记分隔符或行的开头
    • 后跟一个标记分隔符或行尾

我添加了m选项,这意味着^$匹配每行的开头和结尾。

答案 1 :(得分:0)

供参考:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
        assertTokens("+", "a+b+c", "a", "b", "c");
        assertTokens("+", "a?+b+c", "a?+b", "c");
        assertTokens("+", "a??+b+c", "a??", "b", "c");
        assertTokens("+", "a???+b+c", "a???+b", "c");
        assertTokens("+", "a?'??+b+c", "a?'??", "b", "c");

        assertTokens("\\:", "a???:b:c", "a???:b", "c");
        assertTokens("\\:", "a????:b:c", "a????", "b", "c");
    }

    private static void assertTokens(String delim, String data, String... expectedTokens) {
        Pattern pattern = Pattern.compile("(?:[^" + delim + ":?]++|\\?.)+");
        Matcher matcher = pattern.matcher(data);

        List<String> tokens = new ArrayList<>();
        while (matcher.find()) {
            tokens.add(matcher.group());
        }

        if(!Arrays.deepEquals(tokens.toArray(), expectedTokens)) {
            for (String token: tokens) {
                System.out.println(token);
            }
            throw new IllegalStateException("Not equals for " + data);
        }
    }
}