簡體   English   中英

為什么在某些風格的外觀工作中沒有有限的重復?

[英]Why doesn't finite repetition in lookbehind work in some flavors?

我想從dd/mm/yy格式的日期解析中間的2位數字,但也允許日期和月份的單位數字。

這就是我想出的:

(?<=^[\d]{1,2}\/)[\d]{1,2}

我想要一個帶有1或2位數字的1位或2位數字[\\d]{1,2}並在它之前斜線^[\\d]{1,2}\\/

這不適用於許多組合,我已經測試了10/10/10 / 11/12/13等...

但令我驚訝的是(?<=^\\d\\d\\/)[\\d]{1,2}

但是[\\d]{1,2}也應該匹配,如果\\d\\d ,或者我錯了?

關於外觀支持

主要的正則表達式風格對於后視不同有不同的支持; 有些人施加了某些限制,有些甚至根本不支持。

  • Javascript:不支持
  • Python:僅限固定長度
  • Java:僅限有限長度
  • .NET:沒有限制

參考


蟒蛇

在Python中,只支持固定長度的lookbehind,原始模式會引發錯誤,因為\\d{1,2}顯然沒有固定的長度。 你可以通過在兩個不同的固定長度的lookbehinds上交替來“修復”這個,例如:

(?<=^\d\/)\d{1,2}|(?<=^\d\d\/)\d{1,2}

或者也許您可以將兩個lookbehinds作為非捕獲組的替代:

(?:(?<=^\d\/)|(?<=^\d\d\/))\d{1,2}

(請注意,您可以使用\\d而不使用括號)。

也就是說,使用捕獲組可能要簡單得多:

^\d{1,2}\/(\d{1,2})

請注意,如果您只有一個組,則findall將返回組1捕獲的內容。 捕獲組比后觀更受支持,並且通常會導致更易讀的模式(例如在這種情況下)。

此片段說明了以上所有要點:

p = re.compile(r'(?:(?<=^\d\/)|(?<=^\d\d\/))\d{1,2}')

print(p.findall("12/34/56"))   # "[34]"
print(p.findall("1/23/45"))    # "[23]"

p = re.compile(r'^\d{1,2}\/(\d{1,2})')

print(p.findall("12/34/56"))   # "[34]"
print(p.findall("1/23/45"))    # "[23]"

p = re.compile(r'(?<=^\d{1,2}\/)\d{1,2}')
# raise error("look-behind requires fixed-width pattern")

參考


Java的

Java僅支持有限長度的lookbehind,因此您可以像原始模式一樣使用\\d{1,2} 以下代碼段演示了這一點:

    String text =
        "12/34/56 date\n" +
        "1/23/45 another date\n";

    Pattern p = Pattern.compile("(?m)(?<=^\\d{1,2}/)\\d{1,2}");
    Matcher m = p.matcher(text);
    while (m.find()) {
        System.out.println(m.group());
    } // "34", "23"

請注意, (?m)是嵌入的Pattern.MULTILINE因此^匹配每一行的開頭。 另請注意,因為\\是字符串文字的轉義字符,所以必須編寫"\\\\"以在Java中獲得一個反斜杠。


C-夏普

C#支持lookbehind的完整正則表達式。 以下代碼段顯示了如何在lookbehind上使用+重復:

var text = @"
1/23/45
12/34/56
123/45/67
1234/56/78
";

Regex r = new Regex(@"(?m)(?<=^\d+/)\d{1,2}");
foreach (Match m in r.Matches(text)) {
  Console.WriteLine(m);
} // "23", "34", "45", "56"

請注意,與Java不同,在C#中,您可以使用@ -quoted字符串,這樣您就不必轉義\\

為了完整性,以下是您在C#中使用捕獲組選項的方法:

Regex r = new Regex(@"(?m)^\d+/(\d{1,2})");
foreach (Match m in r.Matches(text)) {
  Console.WriteLine("Matched [" + m + "]; month = " + m.Groups[1]);
}

鑒於以前的text ,這打印:

Matched [1/23]; month = 23
Matched [12/34]; month = 34
Matched [123/45]; month = 45
Matched [1234/56]; month = 56

相關問題

除非有一個特定的理由使用在問題中沒有注明的lookbehind,如何簡單地匹配整個事物而只捕獲你感興趣的位呢?

JavaScript示例:

>>> /^\d{1,2}\/(\d{1,2})\/\d{1,2}$/.exec("12/12/12")[1]
"12"

引用regular-expressions.info

壞消息是,大多數正則表達式都不允許您在lookbehind中使用任何正則表達式,因為它們無法向后應用正則表達式。 因此,正則表達式引擎需要能夠在檢查lookbehind之前找出退回的步驟數。

因此,許多正則表達式,包括Perl和Python使用的那些,只允許固定長度的字符串。 您可以使用任何可以預先確定匹配長度的正則表達式。 這意味着您可以使用文字文本和字符類。 您不能使用重復或可選項。 您可以使用交替,但僅當交替中的所有選項具有相同的長度時。

換句話說,你的正則表達式不起作用,因為你在lookbehind中使用了一個可變寬度表達式,而你的正則表達式引擎不支持它。

除了@polygenelubricants列出的那些之外,“僅限固定長度”規則還有兩個例外。 在PCRE(PHP,Apache 等人的正則表達式引擎)和Oniguruma(Ruby 1.9,Textmate)中,lookbehind可以包含一個替換,其中每個替代可以匹配不同數量的字符,只要每個替代的長度是固定的。 例如:

(?<=\b\d\d/|\b\d/)\d{1,2}(?=/\d{2}\b)

請注意,交替必須位於lookbehind子表達式的頂層。 你可能像我一樣,想要分解出共同的元素,就像這樣:

(?<=\b(?:\d\d/|\d)/)\d{1,2}(?=/\d{2}\b)

......但它不起作用; 在頂層,子表達式現在由一個具有非固定長度的替代方案組成。

第二個例外更有用: \\K ,由Perl和PCRE支持。 它實際上意味着“假裝比賽真的從這里開始。” 在正則表達式之前出現的任何東西被視為積極的外觀。 與.NET lookbehinds一樣,沒有任何限制; 普通正則表達式中出現的任何內容都可以在\\K之前使用。

\b\d{1,2}/\K\d{1,2}(?=/\d{2}\b)

但大多數時候,當有人遇到外觀問題時,事實證明他們甚至不應該使用它們。 正如@insin指出的那樣,使用捕獲組可以更容易地解決這個問題。

編輯:幾乎忘了JGSoft,EditPad Pro和PowerGrep使用的正則表達式風格; 像.NET一樣,它具有完全不受限制的外觀,正面和負面。

暫無
暫無

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

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