簡體   English   中英

Java正則表達式 - 匹配模式的第一次出現

[英]Java Regular Expressions - Matching the First Occurrence of a Pattern

我正在將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命令一樣,不能要求登錄,而其他命令可以而且應該(這個奇怪的原因超出了我的問題范圍)。

由於這是一個安全問題,我不能允許用戶只是將&command = shutdown附加到URL並繞過過濾器。

所以我真的需要一個正則表達式,否則我需要重新定義配置規范。

您需要分步執行此操作:

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

(2)檢查匹配是否shutdown

這個經過測試(並且完全注釋)的正則表達式解決方案滿足您的所有要求:

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的更多信息,請參閱我的文章: 正則表達式URI驗證

好。 我非常感謝你們的出色答案! 我嘗試了一些建議,與其他人斗爭,總而言之,我必須同意,即使正確的正則表達式存在,它看起來很糟糕,不可維護,並且可以很好地作為一個討厭的大學練習,但不是在一個真實的系統中組態。

我也意識到,由於此處涉及過濾器,並且過濾器已經解析了自己的URI,因此將所有URI部分粘合到字符串中並將其與正則表達式進行匹配絕對是荒謬的。 我在想什么?

因此,我將重新設計Filter及其配置。

非常感謝,人們! 我很感激幫助:)

諾姆羅特姆。

PS - 我為什么得到一個userXXXX缺口? 很奇怪...

如果你只能接受第一場比賽,你可以使用'\\\\Wcommand=([^&]+)並獲取第一組。

否則,您可以只調用Matcher.find兩次以測試后續匹配,並最終使用第一個匹配,為什么要使用單個復雜正則表達式執行此操作?

如果這可以使用單個正則表達式完成,那么很可能就是這樣; 它將是如此復雜,以至於不可讀,因而無法維護,因為邏輯的意圖將會丟失。 即使它是“記錄”的,對於剛認識Java的人來說,它仍然不那么明顯。

解決這樣的問題是濫用正則表達,就像用錘子驅動螺釘一樣濫用錘子和螺釘。

一個更好的方法是使用URI對象解析整個事物,域和所有並拉出查詢參數,然后編寫一個簡單的循環,遍歷它們並根據您的業務邏輯決定什么是關閉和什么是'噸。 然后它將是簡單的,自我記錄的,可能更有效(不應該是一個問題)。

有些人在面對問題時會想“我知道,我會使用正則表達式”。 現在他們有兩個問題。 - 傑米·扎溫斯基

向下投票你想要的所有,但這個具體例子的最佳解決方案不是正則表達式; 鑒於“澄清”更是如此。

特別是在您必須與人共享代碼的商業環境中,不僅要與您合作,還要在未來與未知的人才庫合作。 “接受”的答案絕不應該通過公司代碼審查。 Zawinski的報價恰恰適用於這種情況!

我不是Java編碼器,但嘗試這個(在Perl中工作)>>

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

要匹配第一次出現的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”限定符不是它們的目的,性能可能不是最好的。

嘗試這個:

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

或者更可讀:

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

正則表達式的主體是一個交替,它匹配關閉命令或名稱不是command的參數。 如果它與shutdown命令匹配,則該分支中的空組“捕獲”空字符串。 它不需要消耗任何東西,因為我們只是用它作為一個復選框,確認順便的參數之一就是關機命令。

負向前瞻 - (?!\\1) - 阻止它匹配兩個或多個關閉命令。 我不知道這是否真的有必要,但這是一個很好的機會來證明(1)如何否定“反向斷言”,以及(2)反向引用可以出現在它在某些情況下引用的組之前(什么是被稱為前向參考 )。

當消耗掉整個URL時,反向引用( \\1 )就像一個零寬度斷言。 如果其中一個參數是command=shutdown ,則反向引用將成功。 否則它將失敗,即使它只是嘗試匹配空字符串,因為它引用的組沒有參與匹配。

但我必須同意其他響應者:當你的正則表達式變得復雜時,你應該認真考慮轉向不同的方法。


編輯:它適合我。 這是演示

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM