java支持if-then-else regex构造(Perl构造)吗?

时间:2011-11-09 22:39:18

标签: java regex perl

尝试编译以下正则表达式时收到PatternSyntaxException:

"bd".matches("(a)?b(?(1)c|d)")

这个正则表达式匹配bd和abc。它与bc不匹配。

任何想法?感谢。

好的,我需要编写正则表达式以匹配接下来的4个字符串:

*date date* date date1*date2

不应该匹配:

*date* date1*date2* *date1*date2 date** ...

但这应该通过单一匹配完成,而不是几次。

请不要发布如下答案:

(date*date)|(*date)|(date*)|(date)

6 个答案:

答案 0 :(得分:7)

想象一下,如果您的语言缺少else语句,但您想模仿它。而不是写

if (condition) { yes part }
else           { no part  }

你必须写

if (condition)   { yes part }
if (!condition)  { no part  }

嗯,这就是你必须要做的,但是在模式中。你在没有条件的Java中做的是你在ELSE块中重复这个条件,但是否定它,它实际上是一个OR块。

因此,举例来说,不是用像Perl这样的语言来编写,而是在模式中使用条件支持:

# definition of \b using a conditional in the pattern like Perl
#
(?(?<=      \w)     # if there is a word character to the left
      (?!   \w)     #    then there must be no word character to the right
  |   (?=   \w)     #    else there must be a  word character to the right
)

你必须在Java中写道:

# definition of \b using a duplicated condition like Java
#
(?:   (?<=  \w)     # if there is a word character to the left
      (?!   \w)     #    then there must be no word character to the right
  |                 # ...otherwise...
      (?<!  \w)     # if there is no word character to the left
      (?=   \w)     #    then there must be a word character to the right
)

您可能会认为这是\b的定义。然后类似于\B的定义,首先使用条件:

# definition of \B using a conditional in the pattern like Perl
#
(?(?<=      \w)     # if there is a word character to the left
      (?=   \w)     #    then there must be a  word character to the right
  |   (?!   \w)     #    else there must be no word character to the right
)

现在通过重复OR分支中的(现在否定的)条件:

# definition of \B using a duplicated condition like Java
#
(?:   (?<=  \w)     # if there is a word character to the left
      (?!   \w)     #    then there must be no word character to the right
  |                 # ...otherwise...
      (?<!  \w)     # if there is no word character to the left
      (?=   \w)     #    then there must be a word character to the right
)

请注意,无论你如何推动它们,\b\B的相应定义都完全取决于\w的定义,永远不会放在\W上,让单独在\s上。

能够使用条件不仅可以节省打字,还可以减少错误的机会。它们也可能是您不关心两次评估病情的情况。

在这里,我利用它来定义几个正则表达式子程序,它们为我提供了一个希腊语原子和相同的边界:

(?(DEFINE)
    (?<greeklish>            [\p{Greek}\p{Inherited}]   )
    (?<ungreeklish>          [^\p{Greek}\p{Inherited}]  )
    (?<greek_boundary>
        (?(?<=      (?&greeklish))
              (?!   (?&greeklish))
          |   (?=   (?&greeklish))
        )
    )
    (?<greek_nonboundary>
        (?(?<=      (?&greeklish))
              (?=   (?&greeklish))
          |   (?!   (?&greeklish))
        )
    )
)

请注意边界和非边界仅使用(&?greeklish),而不是(?&ungreeklish)?你永远不需要做任何只做边界的事情。您可以将not放入您的外观中,就像\b\B一样。

虽然在Perl中,定义一个新的自定义属性\p{IsGreeklish}(以及它的补充\P{IsGreeklish})可能更容易(尽管不那么通用):

 sub IsGreeklish {
     return <<'END';
 +utf8::IsGreek
 +utf8::IsInherited
 END
 }

虽然不是因为Java缺乏对条件的支持,而是因为它的模式语言不允许(DEFINE)块或正则表达式子例程,所以你将无法将其中的任何一个转换为Java。像(?&greeklish)这样的调用 - 实际上,你的模式甚至无法在Java中进行递归。你也不能在Java中定义像\p{IsGreeklish}这样的自定义属性。

当然,Perl正则表达式中的条件可能不仅仅是外观:它们甚至可以是代码块来执行 - 这就是为什么你当然不希望被迫两次评估相同的条件,以免它产生副作用。这不适用于Java,因为它无法做到这一点。你不能混淆模式和代码,这比你养成习惯之前想象的要多。

你可以用Perl正则表达式引擎做很多事情,你可以用别的语言来做,这只是其中的一部分。毫无疑问,新编程Perl的第4版中大大扩展的Regexes章节,加上完全重写的Unicode章节,现在紧跟Regexes章节(已被提升为内核的一部分),具有组合页面数量类似130页的东西,所以从第3版开始,模式匹配的旧章节的长度增加了一倍。

您刚才看到的是新版第4版的内容,应该会在下个月左右发布。

答案 1 :(得分:3)

Java不支持条件,但是你可以在它的位置使用一些技巧。看看:

String[] test = { "abc", "abd", "bc", "bd", "ad", "ac" };
for (String s : test)
{
  System.out.printf("%-4s: %b%n", s, s.matches("(?:a())?b(\\1c|(?!\\1)d)"));
}

输出:

abc : true
abd : false
bc  : false
bd  : true
ad  : false
ac  : false

如果字符串不以a开头,则第一个捕获组不参与匹配,反向引用\1失败,就像条件组中的(1)一样。否则它匹配一个空字符串,与该组相同。

条件的另一个方面是它执行独占 OR;如果条件为真,则第二个分支成功(因此abd不匹配)。第二个分支中的否定反向引用实现了这一点。

这个技巧几乎适用于所有流行的Perl派生版本,包括Java,.NET,Python,PHP(PCRE)和Ruby(Oniguruma)。它在像JavaScript和ActionScript这样的ECMAScript实现中不起作用。


编辑:好的,你已经添加了一些示例字符串,并且@sln已经展示了如何将它们与伪条件匹配,但我想知道你是否真的需要它们。您的“有效”字符串似乎至少包含一个date,其间穿插最多一个*,可以表示为

^\*date|date(?:\*(?:date)?)?$

这是 demo ,其中包括@ sln的正则表达式以及我的。

答案 2 :(得分:2)

根据OP的编辑和样本添加新答案:

ok i need to write regex to match next 4 strings:
*date date* date date1*date2
should not match:
*date* date1*date2* *date1*date2 date** ...

如果我认为我理解你,你可以使用基于Alan Moore伪条件技巧的正则表达式。

^(?:[*]())?date(?:(?!\1)[*](?:date)?|)$这样的东西可能有用 我认为'date'是样本中唯一的文本,样本中的每组非空格字符都是不同的文本行。

在您传递的文本中,只有一个表单需要伪条件。那是'日期*日期'。所以,我在下面包含了一个Perl示例(因为我没有Java编译器),为了清晰起见,扩展了正则表达式。

use strict;
use warnings;

my @samps = qw(

*date
 date*
 date
 date*date
*date*
 date*date*
*date*date
 date**
);

for my $str (@samps)
{
   print "\n'$str'\n";

   if ($str =~
       /
        ^          # Begin of string
        (?:             # Expr grouping
            [*]()          # Asterisk found then DEFINE capture group 1 as empty string
        )?              # End expr group, optional, if asterisk NOT found, capture group 1 stays UNDEFined
        date   #  'data'
        (?:             # Expr grouping
            (?!\1)           # Pseudo conditional: If no asterisk (group 1 is UNDEF), then
            [*](?:date)?     # look for '*' folowed by optional 'data'
          |               # OR,
        )                    # Asterisk or not, should be nothing here
        $          # End of string
      /x)

     {
         print "matched: '$str'\n";
     }
}

输出:

'*date'
matched: '*date'

'date*'
matched: 'date*'

'date'
matched: 'date'

'date*date'
matched: 'date*date'

'*date*'

'date*date*'

'*date*date'

'date**'

答案 3 :(得分:1)

如果没有这种设施,你不可能继续下去。我希望你不会陷入试图将多种功能集成到一个正则表达式中的常见陷阱?

请描述一下您的问题。我确信有一个比使用外部库来实现您设计的解决方案更好的选择。

答案 4 :(得分:0)

阅读Java 1.5 Pattern specJava 1.6 Pattern specJava 7 spec它似乎没有if-then-else结构。

可以在this blog post找到问题中正则表达式的解释和(不支持条件的其他语言的一些不同选项)。可以在this page

上阅读完整的解释(并进一步确认Java不支持)

您可能会寻找第三方库来进行模式匹配,但它不会与String类集成。

答案 5 :(得分:0)

根据维基百科文章 here ,在引擎比较表中,java的doesen不做条件。