正则表达式 - 否定前瞻

时间:2014-02-07 18:58:06

标签: java regex string pattern-matching

使用以下表达式:

(?<!XYZ\d{8})(?>REF[A-Z]*)?(\d{3}+)(\d{6}+)(\d{3}+)

我得到意想不到的比赛。请解释为什么会发生以下情况:

  • 输入XYZ12345678123456789123 - 123456781234上的匹配项 - 我希望它仅匹配123456789123,因为它是唯一没有(?<!XYZ\d{8})
  • 的序列

奇怪的是,如果我使用XYZ12345678REF123456789876作为输入,则会返回123456789876但不是REF123456789876的匹配项。它正确地忽略了XYZ12345678,但它没有选择可选的REF字符。

基本上我想要实现的是从包含两个标识符的字符串中提取12位数字标识符。第一个标识符的格式为XYZ\d{8},第二个标识符的格式为(?>REF[A-Z]*)?(\d{3}+)(\d{6}+)(\d{3}+)

为了避免匹配XYZ12345678123456789123等字符串中错误的12位数,我想说 - 只要数字不是XYZ\d{8}类型标识符的一部分,就得到12位数字。

修改

以下是我想要实现的几个例子

XYZ12345678123456789123  match on 123456789123
123456789123 match on 123456789123
XYZ12345678REF123456789123 should match on REF123456789123
12345678912 no match because not 12 digits
REF123456789123 match on REF123456789123
REF12345678912 no match because not 12 digits
XYZ12345678123456789123ABC match on 123456789123
XYZ123456789123  No match
XYZ1234567891234  no match

3 个答案:

答案 0 :(得分:2)

你几乎在那里。将(?<!XYZ\\d{8})更改为(?<!XYZ\\d{0,7})。您需要检查您的匹配项是否不是之前标识符XYZ\\d{8}的一部分,这意味着它不能

  • XYZ
  • XYZ1
  • XYZ12
  • ...
  • XYZ1234567

之前。


基于您的示例进行演示

String[] data ={
        "XYZ12345678123456789123",          //123456789123
        "123456789123",                     //123456789123
        "XYZ12345678REF123456789123 ",      //REF123456789123
        "12345678912",                      //no match because not 12 digits
        "REF123456789123",                  //REF123456789123
        "REF12345678912",                   //no match because not 12 digits
        "XYZ12345678123456789123ABC",       //123456789123
        "XYZ123456789123",                  //no match
        "XYZ1234567891234",                 //no match
};


Pattern p = Pattern.compile("(?<!XYZ\\d{0,7})(?>REF[A-Z]*)?(\\d{3}+)(\\d{6}+)(\\d{3}+)");
for (String s:data){
    System.out.printf("%-30s",s);
    Matcher m = p.matcher(s);
    while (m.find())
        System.out.print("match: "+m.group());
    System.out.println();
}

输出:

XYZ12345678123456789123       match: 123456789123
123456789123                  match: 123456789123
XYZ12345678REF123456789123    match: REF123456789123
12345678912                   
REF123456789123               match: REF123456789123
REF12345678912                
XYZ12345678123456789123ABC    match: 123456789123
XYZ123456789123               
XYZ1234567891234              

答案 1 :(得分:1)

引擎开始查看字符串中的第一个字符 如果字符串是“ABCDEF”且正则表达式是(?<!C)...
查看A,它看到左侧没有C

断言得到满足,然后匹配ABC

断言只是在它所处的当前位置测试它周围的字符。
它们不会强制引擎首先找到C并匹配后面的字符。

修改

从你的例子中你需要这样的东西,这是固定的 如果没有锚定,可能会更难。

此外,Java没有分支重置,因此您必须查看哪个组 群集匹配。

 #  "^(?:(?:XYZ\\d{8})(\\d{3})(\\d{6})(\\d{3})|(?:REF)(\\d{3})(\\d{6})(\\d{3})|(\\d{3})(\\d{6})(\\d{3}))"

 ^ 
 (?:
      (?: XYZ \d{8} )
      ( \d{3} )            # (1)
      ( \d{6} )            # (2)
      ( \d{3} )            # (3)
   |  
      (?: REF )
      ( \d{3} )            # (4)
      ( \d{6} )            # (5)
      ( \d{3} )            # (6)
   |  
      ( \d{3} )            # (7)
      ( \d{6} )            # (8)
      ( \d{3} )            # (9)
 )

替代方案,

 #  "^(?:(?:XYZ\\d{8})|(?:REF))?(\\d{3})(\\d{6})(\\d{3})"

 ^ 
 (?:
      (?: XYZ \d{8} )
   |  (?: REF )
 )?
 ( \d{3} )                 # (1)
 ( \d{6} )                 # (2)
 ( \d{3} )                 # (3)

答案 2 :(得分:0)

你可以检查它的匹配是不是以前的标识符XYZ \ d {8}的一部分,这意味着它不能

XYZ
XYZ1
XYZ12
...
XYZ1234567

之前。

此外,Java没有分支重置,因此您必须查看哪个组 群集匹配。

我会做出改变

 (?<!XYZ\\d{8})    to    (?<!XYZ\\d{0,7}).

希望这会有所帮助。