regex - 正則包含 - 正則表達式匹配不包含單詞的行?




正則不包含 (18)

基準

我決定評估一些呈現的選項並比較它們的性能,以及使用一些新功能。 .NET Regex引擎的基準測試: http://regexhero.net/tester/http://regexhero.net/tester/

基准文字:

前7行不匹配,因為它們包含搜索的表達式,而較低的7行應該匹配!

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

結果:

結果是每秒迭代次數為3次運行的中位數 - 更大的數字=更好

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

由於.NET不支持動作動詞(* FAIL等),我無法測試解決方案P1和P2。

摘要:

我嘗試測試大多數提議的解決方案,某些優化可能適用於某些單詞。 例如,如果搜索字符串的前兩個字母不相同,則答案03可以擴展為^(?>[^R]+|R+(?!egex Hero))*$從而獲得較小的性能增益。

但總體上最具可讀性和性能最快的解決方案似乎是05使用條件語句或04使用積極量詞。 我認為Perl解決方案應該更快,更容易閱讀。

我知道可以匹配一個單詞,然後使用其他工具(例如grep -v )反轉匹配。 但是,我想知道是否可以使用正則表達式匹配包含特定單詞的行(例如hede)。

輸入:

hoho
hihi
haha
hede

碼:

grep "<Regex for 'doesn't contain hede'>" input

期望的輸出:

hoho
hihi
haha

如何使用PCRE的回溯控制動詞來匹配不包含單詞的行

這是我以前沒見過的方法:

/.*hede(*COMMIT)^|/

這個怎麼運作

首先,它試圖在線上找到“hede”。如果成功,此時,(*COMMIT)告訴引擎,不僅在發生故障時不回溯,而且在這種情況下也不再嘗試進一步匹配。然後,我們嘗試匹配一些不可能匹配的東西(在這種情況下^)。

如果一行不包含“hede”,則第二個替代方案(空子模式)成功匹配主題字符串。

這種方法並不比負面的前瞻更有效,但我想我會把它扔在這里以防有人發現它很漂亮並且發現它用於其他更有趣的應用程序。


OP未指定或標記帖子以指示正在使用Regex的上下文(編程語言,編輯器,工具)。

對我來說,有時我需要在使用Textpad編輯文件時執行此操作。

Textpad支持一些正則表達式,但不支持前瞻或後瞻,因此需要幾個步驟。

如果我想保留所有包含字符串hede ,我會這樣做:

1.搜索/替換整個文件,以在包含任何文本的每一行的開頭添加唯一的“標記”。

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2.刪除包含字符串hede所有行(替換字符串為空):

    Search string:<@#-unique-#@>.*hede.*\n  
    Replace string:<nothing>  
    Replace-all  

3.此時,所有剩餘的行都不包含字符串hede 。 從所有行中刪除唯一的“Tag”(替換字符串為空):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

現在您有原始文本,其中所有行都包含刪除的字符串hede

如果我正在尋找Do Something Else只有那些包含字符串hede ,我會這樣做:

1.搜索/替換整個文件,以在包含任何文本的每一行的開頭添加唯一的“標記”。

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2.對於包含字符串hede所有行,刪除唯一的“Tag”:

    Search string:<@#-unique-#@>(.*hede)
    Replace string:\1  
    Replace-all  

3.此時,所有以唯一“標記”開頭的行都不包含字符串hede 。 我現在可以做我的其他東西了

4.完成後,我從所有行中刪除唯一的“Tag”(替換字符串為空):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

如果您只是將它用於grep,則可以使用grep -v hede來獲取所有不包含hede的行。

ETA哦,重讀這個問題, grep -v可能就是你所說的“工具選項”。


通過PCRE動詞(*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

這將完全跳過包含確切字符串hede並匹配所有剩餘行的行。

DEMO

執行部件:

讓我們通過將它分成兩部分來考慮上面的正則表達式。

  1. |之前的部分 符號。 部分不應該匹配

    ^hede$(*SKIP)(*F)
    
  2. 部分後| 符號。 部分應該匹配

    ^.*$
    

第1部分

正則表達式引擎將從第一部分開始執行。

^hede$(*SKIP)(*F)

說明:

  • ^斷言我們剛開始。
  • hede匹配字符串hede
  • $斷言我們在線端。

所以包含字符串hede行將匹配。 一旦正則表達式引擎看到以下(*SKIP)(*F)注意:你可以寫(*F)(*FAIL) )動詞,它會跳過並使匹配失敗。 | 在PCRE動詞旁邊添加了一個名為alteration或邏輯OR運算符,它匹配所有行中每個字符之間的所有邊界,除了該行包含精確的字符串hede 。 請在here查看演示。 也就是說,它嘗試匹配剩餘字符串中的字符。 現在第二部分中的正則表達式將被執行。

第2部分

^.*$

說明:

  • ^斷言我們剛開始。 即,它匹配除了hede行中的那一行之外的所有行開始。 請在here查看演示。
  • .*在多線模式下, . 將匹配除換行符或回車符之外的任何字符。 並且*將重複前一個字符零次或多次。 所以.*會匹配整條線。 請在here查看演示。

    嘿,為什麼你添加。*而不是。+?

    因為.*會匹配一個空白行但是.+將不匹配空白。 我們希望匹配除了hede之外的所有行,輸入中也可能存在空行。 所以你必須使用.*而不是.+.+會重複前一個字符一次或多次。 請參閱.*匹配here的空白行。

  • 這裡不需要行結束錨。


上述(?:(?!hede).)*很棒,因為它可以錨定。

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

但在這種情況下,以下就足夠了:

^(?!.*hede)                    # A line without hede

這種簡化已準備好添加“AND”子句:

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same

以下功能將幫助您獲得所需的輸出

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>

使用負向前瞻,正則表達式可以匹配不包含特定模式的內容。 Bart Kiers回答並解釋了這一點。 很棒的解釋!

但是,根據Bart Kiers的回答,前瞻部分將在匹配任何單個字符時測試前方1到4個字符。 我們可以避免這種情況,讓前瞻部分檢查整個文本,確保沒有'hede',然後正常部分(。*)可以一次吃掉整個文本。

這是改進的正則表達式:

/^(?!.*?hede).*$/

注意負前瞻部分中的(*?)惰性量詞是可選的,你可以使用(*)貪心量詞,取決於你的數據:如果'hede'確實存在,並且在文本的開頭一半,懶惰量詞可以更快; 否則,貪婪量詞會更快。 但是,如果'hede'不存在,兩者都會相等。

這是演示代碼

有關前瞻的更多信息,請查看精彩的文章: 掌握Lookahead和Lookbehind

另外,請查看RegexGen.js ,它是一個JavaScript正則表達式生成器,有助於構建複雜的正則表達式。 使用RegexGen.js,您可以以更易讀的方式構造正則表達式:

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);

如果您希望正則表達式測試整個字符串匹配時失敗,則以下內容將起作用:

^(?!hede$).*

例如 - 如果你想允許除“foo”之外的所有值(即“foofoo”,“barfoo”和“foobar”將通過,但“foo”將失敗),請使用: ^(?!foo$).*

當然,如果你正在檢查確切的相等性,那麼在這種情況下更好的通用解決方案是檢查字符串是否相等,即

myStr !== 'foo'

如果您需要任何正則表達式功能(這裡是不區分大小寫和範圍匹配),您甚至可以將否定置於測試之外

!/^[a-f]oo$/i.test(myStr)

然而,在需要正面的正則表達式測試的情況下(可能通過API),此答案頂部的正則表達式解決方案可能會有所幫助。


我不明白這裡需要復雜的正則表達式甚至是前瞻性:

/hede|^(.*)$/gm

不要在捕獲組中放入你不想要的東西,而是將其中一個用於其他所有東西。這將匹配所有不包含“hede”的行。


有了這個,你可以避免在每個位置測試前瞻:

/^(?:[^h]+|h++(?!ede))*+$/

相當於(for .net):

^(?>(?:[^h]+|h+(?!ede))*)$

老答案:

/^(?>[^h]+|h+(?!ede))*$/

正則表達式不支持逆匹配的概念並不完全正確。 您可以使用負面外觀來模倣此行為:

^((?!hede).)*$

上面的正則表達式將匹配任何字符串,或沒有換行符的行, 包含(子)字符串'hede'。 如上所述,這不是正則表達式(或應該做的)“好”的東西,但仍然可能的。

如果您還需要匹配換行符,請使用DOT-ALL修飾符 (以下模式中的尾隨s ):

/^((?!hede).)*$/s

或者內聯使用:

/(?s)^((?!hede).)*$/

(其中/.../是正則表達式分隔符,即不是模式的一部分)

如果DOT-ALL修飾符不可用,則可以使用字符類[\s\S]模仿相同的行為:

/^((?!hede)[\s\S])*$/

說明

字符串只是n字符的列表。 在每個字符之前和之後,都有一個空字符串。 因此, n字符的列表將具有n+1空字符串。 考慮字符串"ABhedeCD"

    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│
    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘

index    0      1      2      3      4      5      6      7

e是空字符串。 正則表達式(?!hede). 展望未來,看看是否沒有子串"hede"可以看到,如果是這種情況(所以看到別的東西),那麼. (點)將匹配除換行符之外的任何字符。 環視也稱為零寬度斷言,因為它們不消耗任何字符。 他們只斷言/驗證某些東西。

所以,在我的例子中,每個空字符串首先被驗證,以確定在字符被消費之前是否沒有"hede" . (點)。 正則表達式(?!hede). 只做一次,所以它被包裹在一個組中,重複零次或多次: ((?!hede).)* 。 最後,輸入的開始和結束被錨定以確保消耗整個輸入: ^((?!hede).)*$

正如您所看到的,輸入"ABhedeCD"將失敗,因為在e3 ,正則表達式(?!hede)失敗(前面有"hede" !)。


給出的答案非常好,只是一個學術觀點:

理論計算機科學意義上的正則表達式並不是這樣的。 對他們來說,它必須看起來像這樣:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

這只是一個完全匹配。 為子匹配做這件事甚至會更加尷尬。


自從引入ruby-2.4.1以來,我們可以在Ruby的正則表達式中使用新的Absent Operator

來自官方doc

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

因此,在您的情況下, ^(?~hede)$為您完成工作

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]

這裡有一個很好的解釋為什麼否定任意正則表達式並不容易。 我不得不同意其他答案:如果這不是一個假設的問題,那麼正則表達式不是正確的選擇。


也許你會在Google上找到這個,同時嘗試編寫一個能夠匹配包含子字符串的一行(而不是整行)的正則表達式。請稍等一下弄清楚,所以我會分享:

給定一個字符串: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

我想匹配<span>不包含子字符串“bad”的標籤。

/<span(?:(?!bad).)*?>會匹配<span class=\"good\"><span class=\"ugly\">

請注意,括號中有兩組(圖層):

  • 最裡面的是負向前瞻(它不是捕獲組)
  • 最外層被Ruby解釋為捕獲組,但我們不希望它成為捕獲組,因此我添加了?:在它開始時它不再被解釋為捕獲組。

Ruby中的演示:

s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]

使用ConyEdit,您可以使用命令行cc.gl !/hede/獲取不包含正則表達式匹配的行,或使用命令行cc.dl /hede/刪除包含正則表達式匹配的行。他們有相同的結果。


更簡單的解決方案是使用not運算符

你的if語句需要匹配“contains”而不匹配“excludes”。

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

我相信RegEx的設計師期望使用非運營商。





regex-group