Java正则表达式 - 匹配模式的第一次出现

时间:2012-10-05 16:12:05

标签: java regex

我正在将URL与正则表达式进行匹配,测试它们是否反映了“shutdown”命令。

这是执行关闭的URL:

/exec?debug=true&command=shutdown&f=0

这是执行关闭的另一个合法但令人困惑的URL:

/exec?commando=yes&zcommand=34&command=shutdown&p

现在,我必须确保只有一个 command = ... 参数,它是 command = shutdown 。或者,我可以确保第一个 命令= ... 参数 command = shutdown

这是我对所请求的正则表达式的测试:

/exec?version=0.4&command=shutdown&out=JSON&zcommand=1

应匹配

/exec?version=0.4&command=startup&out=JSON&zcommand=1&commando=shutdown

无法匹配

/exec?command=shutdown&out=JSON

应匹配

/exec?version=0.4&command=admin&out=JSON&zcommand=1&command=shutdown

无法匹配

这是我的基线 - 一个通过上述测试的正则表达式 - 除了最后一个以外的所有:

^/exec?(.*\&)*command=shutdown(\&.*)*$

问题是出现了多个command = ...,其中第一个没有关闭。

我尝试使用lookbehind:

^/exec?(.*\&)*(?<!(\&|\?)command=.*)command=shutdown(\&.*)*$

但我得到了:

Look-behind group does not have an obvious maximum length near index 31

我甚至尝试过原子分组。无济于事。我不能使下面的表达式不匹配:

/exec?version=0.4&command=admin&out=JSON&zcommand=1&command=shutdown

任何人都可以帮助处理通过所有测试的正则表达式吗?

澄清

我知道我欠你一些背景。

我的任务是配置一个过滤器来保护我们所有系统的servlet的入口,并验证是否有一个开放的HTTP会话(换句话说:已成功登录)。该过滤器还允许配置哪些URL不需要登录。

有些例外很简单:/ login不需要登录。对localhost的调用不需要登录。

但有时它会变得复杂。就像shutdown命令一样,不能要求登录,而其他命令可以而且应该(这个奇怪的原因超出了我的问题范围)。

由于这是一个安全问题,我不能允许用户只是将&amp; command = shutdown附加到URL并绕过过滤器。

所以我真的需要一个正则表达式,否则我需要重新定义配置规范。

8 个答案:

答案 0 :(得分:1)

您需要在 多个 步骤中执行此操作:

(1) 查找^(?=\/exec\?).*?(?<=[?&])command=([^&]+)的匹配

(2) 检查匹配是否为shutdown

答案 1 :(得分:1)

这个经过测试(并且完全注释)的正则表达式解决方案满足您的所有要求:

import java.util.regex.*;
public class TEST {
    public static void main(String[] args) {
        Pattern re = Pattern.compile(
            "  # Match URI having command=shutdown query variable value. \n" +
            "  ^                          # Anchor to start of string.   \n" +
            "  (?:[^:/?\\#\\s]+:)?        # URI scheme (Optional).       \n" +
            "  (?://[^/?\\#\\s]*)?        # URI authority (Optional).    \n" +
            "  [^?\\#\\s]*                # URI path.                    \n" +
            "  \\?                        # Literal start of URI query.  \n" +
            "    # Match var=value pairs preceding 'command=xxx'.        \n" +
            "  (?:                        # Zero or more 'var=values'    \n" +
            "    (?!command=)             # only if not-'command=xxx'.   \n" +
            "    [^&\\#\\s]*              # Next var=value.              \n" +
            "    &                        # var=value separator.         \n" +
            "  )*                         # Zero or more 'var=values'    \n" +
            "  command=shutdown           # variable and value to match. \n" +
            "    # Match var=value pairs following 'command=shutdown'.   \n" +
            "  (?:                        # Zero or more 'var=values'    \n" +
            "    &                        # var=value separator.         \n" +
            "    (?!command=)             # only if not-'command=xxx'.   \n" +
            "    [^&\\#\\s]*              # Next var=value.              \n" +
            "  )*                         # Zero or more 'var=values'    \n" +
            "  (?:\\#\\S*)?               # URI fragment (Optional).     \n" +
            "  $                          # Anchor to end of string.", 
            Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.COMMENTS);
        String s = "/exec?version=0.4&command=shutdown&out=JSON&zcommand=1";
            // Should match
//      String s = "/exec?version=0.4&command=startup&out=JSON&zcommand=1&commando=shutdown";
            // Should fail to match 
//      String s = "/exec?command=shutdown&out=JSON";
            // Should match
//      String s = "/exec?version=0.4&command=admin&out=JSON&zcommand=1&command=shutdown";
        // Should fail to match";
        Matcher m = re.matcher(s);
        if (m.find()) {
            // Successful match
            System.out.print("Match found.\n");
        } else {
            // Match attempt failed
            System.out.print("No match found.\n");
        } 
    }
}

上述正则表达式匹配任何具有任何方案,权限,路径,查询或片段组件的RFC3986有效URI,但它必须具有一个(且仅一个)查询"command"变量,其值必须完全相同,但不区分大小写:"shutdown"

精心制作的复杂正则表达式在使用适当的缩进和注释步骤(如上所示)编写时使用非常精细(并且可维护)。 (有关使用正则表达式验证URI的更多信息,请参阅我的文章:Regular Expression URI Validation

答案 2 :(得分:1)

确定。我非常感谢你们的出色答案!我尝试了一些建议,与其他人斗争,总而言之,我必须同意,即使正确的正则表达式存在,它看起来很糟糕,不可维护,并且可以很好地作为一个讨厌的大学练习,但不是在一个真实的系统中配置。

我也意识到,由于此处涉及过滤器,并且过滤器已经解析了自己的URI,因此将所有URI部分粘合到字符串中并将其与正则表达式进行匹配绝对是荒谬的。我在想什么?

因此我将重新设计过滤器及其配置。

非常感谢,人们!我很感激帮助:)

Noam Rotem。

P.S。 - 为什么我会收到userXXXX缺口?很奇怪......

答案 3 :(得分:0)

如果你可以接受第一场比赛,你可以使用'\\Wcommand=([^&]+)并获取第一组。

否则,您可以只调用Matcher.find两次以测试后续匹配,并最终使用第一个匹配,为什么要使用单个复杂正则表达式执行此操作?

答案 4 :(得分:0)

如果可以使用单个正则表达式完成此操作,那么很可能;由于逻辑的意图将会丢失,因此它将变得如此复杂,以至于无法读取,因而无法维护。即使它是“记录”的,对于刚认识Java的人来说,它仍然不那么明显。

  

解决这样的问题就是滥用正则表达式   因为用锤子驱动螺钉会滥用锤子和螺钉   两者。

更好的方法是使用URI对象解析整个事物,域和所有内容并拉出查询参数,然后编写一个简单的循环,遍历它们并根据您的业务逻辑决定什么是关闭,什么不是。然后它将是简单的,自我记录的,可能更有效(不应该是一个问题)。

  

有些人在遇到问题时会想“我知道,我会用   正则表达式。“现在他们有两个问题。 - Jamie Zawinski

向下投票你想要的所有,但这个特定例子的最佳解决方案不是正则表达式;鉴于“澄清”更是如此。

特别是在您必须与人共享代码的商业环境中,不仅要与您合作,还要在未来与未知的人才库合作。 “接受”的答案绝不应该通过公司代码审查。 Zawinski的报价恰恰适用于这种情况!

答案 5 :(得分:0)

我不是Java编码器,但尝试这个(在Perl中工作)&gt;&gt;

^(?=\/exec\?)(?:[^&]+(?<![?&]command)=[^&]+&)*(?<=[?&])command=shutdown(?:&|$)

答案 6 :(得分:0)

要匹配第一次出现的command = shutdown,请使用:

Pattern.compile("^((?!command=).)+command=shutdown.*$");

结果如下:

"/exec?version=0.4&command=shutdown&out=JSON&zcommand=1" => false
"/exec?command=shutdown&out=JSON" => true
"/exec?version=0.4&command=startup&out=JSON&zcommand=1&commando=shutdown" => false
"/exec?commando=yes&zcommand=34&command=shutdown&p" => false

如果要匹配仅包含一个'command ='的字符串,请使用:

Pattern.compile("^((?!command=).)+command=shutdown((?!command=).)+$");

请注意,在正则表达式中使用“not”限定符不是它们的目的,性能可能不是最好的。

答案 7 :(得分:-1)

试试这个:

Pattern p = Pattern.compile(
    "^/exec\\?(?:(?:(?!\\1)command=shutdown()|(?!command=)\\w+(?:=[^&]+)?)(?:&|$))+$\\1");

或者更可读:

^/exec\?
(?:
  (?:
    (?!\1)command=shutdown()
    |
    (?!command=)\w+(?:=[^&]+)?
  )
  (?:&|$)
)+$
\1

正则表达式的主体是一个交替,它匹配关闭命令或名称不是command的参数。如果它与shutdown命令匹配,则该分支中的空组“捕获”空字符串。它不需要消耗任何东西,因为我们只将它用作复选框,确认 en passant 其中一个参数是关闭命令。

否定前瞻 - (?!\1) - 阻止它匹配两个或多个关闭命令。我不知道这是否真的有必要,但这是一个很好的机会来证明(1)如何否定“反向断言”,以及(2)反向引用可以出现在它在某些情况下引用的组之前(什么是称为前向参考)。

当使用整个URL时,反向引用(\1)的作用类似于零宽度断言。如果其中一个参数为command=shutdown,则反向引用将成功。否则它会失败,即使它只是尝试匹配一个空字符串,因为它引用的组没有参与匹配。

但我必须同意其他响应者:当你的正则表达式变得复杂时,你应该认真考虑改用不同的方法。


编辑:它对我有用。这是 demo