[英]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.