【问题标题】:Regex to find last occurrence of pattern in a string正则表达式查找字符串中模式的最后一次出现
【发布时间】:2017-01-26 09:14:58
【问题描述】:

我的字符串是这样的:

"as.asd.sd fdsfs. dfsd  d.sdfsd. sdfsdf sd   .COM"

我只想匹配最后一个句点之前的最后一段空白(。)

到目前为止,我能够捕获空白,但不是 非常 最后出现的使用:

\s+(?=\.\w)

我怎样才能让它不那么贪婪?

【问题讨论】:

标签: regex regex-greedy


【解决方案1】:

在一般情况下,您可以使用以下方案匹配任何模式的最后一次出现

pattern(?![\s\S]*pattern)
(?s)pattern(?!.*pattern)
pattern(?!(?s:.*)pattern)

[\s\S]* 尽可能匹配任何零个或多个字符。 (?s)(?s:.) 可以与支持这些结构的正则表达式引擎一起使用,以便使用 . 匹配任何字符。

在这种情况下,而不是\s+(?![\s\S]*\s),您可以使用

\s+(?!\S*\s)

请参阅regex demo。注意\s\S 是逆类,因此在这里使用[\s\S]* 没有意义,\S* 就足够了。

详情

  • \s+ - 一个或多个空格字符
  • (?!\S*\s) - 后面没有紧跟任何 0 个或多个非空白字符,然后是一个空格。

【讨论】:

  • 为什么(?s) 或令人困惑的[\s\S] 而不是简单的.?恕我直言,仅当您需要 . 匹配换行符时才需要它们。
  • @pabouk 没错,看标题:正则表达式查找字符串中最后出现的模式,字符串可以包含换行符。
【解决方案2】:

你可以这样尝试:

(\s+)(?=\.[^.]+$)

(?=\.[^.]+$) 正向向前寻找一个点和除行尾点以外的字符。

演示:

https://regex101.com/r/k9VwC6/3

【讨论】:

    【解决方案3】:
    "as.asd.sd ffindMyLastOccurrencedsfs. dfindMyLastOccurrencefsd  d.sdfsd. sdfsdf sd   ..COM"
    
    .*(?=((?<=\S)\s+)).*
    
    replaced by `>\1<`
    
    >   <
    

    作为一个更普遍的例子

    这个例子定义了几个针,并找到其中任何一个的最后一次出现。在这个例子中,针是:

    • 定义字findMyLastOccurrence
    • 空格(?&lt;=\S)\s+
    • (?&lt;=[^\.])\.+
    "as.asd.sd ffindMyLastOccurrencedsfs. dfindMyLastOccurrencefsd  d.sdfsd. sdfsdf sd   ..COM"
    
    .*(?=(findMyLastOccurrence|(?<=\S)\s+|(?<=[^\.])\.+)).*
    
    replaced by `>\1<`
    
    >..<
    

    说明:

    第 1 部分 .*

    • 是贪婪的,只要找到针就找到一切。因此,它还会捕获所有出现的针,直到最后一根针。

    编辑添加:

    • 如果我们对首发感兴趣,可以写.*?防止贪心

    第 2 部分 (?=(findMyLastOccurrence|(?&lt;=\S)\s+|(?&lt;=[^\.])\.+|(?&lt;=**Not**NeedlePart)NeedlePart+))

    • 为贪婪的“find-all”定义了“break”条件。它由几个部分组成:
      (?=(needles))
      • 正向前瞻:确保先前找到的所有内容都紧随其后 findMyLastOccurrence|(?&lt;=\S)\s+|(?&lt;=[^\.])\.+)|(?&lt;=**Not**NeedlePart)NeedlePart+
      • 我们正在寻找的几根针。针本身就是图案。
      • 如果我们寻找空格、点或其他针状零件的集合,我们正在寻找的模式实际上是:任何东西 不是 针状零件, 后跟一个或多个 needleparts(因此 needlepart 为 +)。请参阅用 \S, actual dot 否定的空格 \s 的示例。用 [^.]
      • 否定

    第 3 部分 .*

    • 因为我们对剩余部分不感兴趣,所以我们捕获它并且不再使用它。我们可以用括号捕获它并将其用作另一个组,但这超出了这里的范围

    【讨论】:

      【解决方案4】:

      常见问题的简单解决方案

      我读过的所有答案都离题,过于复杂,或者只是不正确。这个问题是一个常见的问题,正则表达式提供了一个简单的解决方案。

      分解一般问题


      1. 字符串

        • 一般的问题是存在一个包含多个字符的字符串
      2. 子字符串

        • 字符串中有一个由几个字符组成的子字符串。通常这是一个文件扩展名(.c.ts.json)或顶级域(.com、@987654327 @ 或 .io),但它可能像 MC Donald's Mulan Szechuan Sauce 一样任意。关键是,它可能并不总是那么简单。
      3. 之前的差异(最重要的部分)

      • 变异前 是一个或多个任意字符,总是出现在子字符串之前。在这个问题中,之前的方差是未知数量的空白。这是一个差异,因为需要匹配的空白数量会有所不同(或具有动态数量)。

      参考问题描述解决方案


      (解决方案第 1 部分)

      在使用正则表达式时,通常需要逆向工作。

      我们将从上述问题的结尾开始,然后向后工作;我们将从 The Before Variance(或 #3)开始

      因此,如上所述,The Before Variance 是未知数量的空白。我们知道它包含空白,但我们不知道有多少,因此我们将使用 Any Whitespce 的元序列一个或多个量词

      • “任何空白”的元序列是 \s
      • “一个或多个”量词+

      所以我们从...开始

      注意:在 ECMAS 正则表达式中,/ 字符就像字符串周围的引号。
      const regex = /\s+/g
      

      我还加入了g 来告诉引擎将全局标志设置为真。为简洁起见,我不会解释标志,但如果您不知道全局标志的作用,您应该使用 DuckDuckGo。



      (解决方案第 2 部分)

      请记住,我们是在逆向工作,所以接下来要关注的部分是子字符串。在这个问题中它是.com,但作者可能希望它与具有方差的值匹配,而不仅仅是静态字符串.com,因此我将在下面详细讨论,但为了保持专注,我们现在可以使用.com

      我们有必要在这里使用一个称为零长度断言的概念。我们需要一个“零长度断言”,因为我们有一个重要的子字符串,但不是我们想要匹配的。 “零长度断言”允许我们移动正则表达式引擎正在查看的字符串中的点,而无需匹配任何字符即可到达那里。

      我们要使用的Zero-Length Assertion叫做LOOK AHEAD,它的语法如下。

      前瞻语法:(?=Your-SubStr-Here)

      我们将使用前瞻来匹配分配给前瞻的模式之前的方差,这将是我们的子字符串。结果如下所示:

      const regex = /\s+(?=\.com)/gi
      

      我添加了不敏感标志来告诉引擎不关心字母的大小写,换句话说;正则表达式/\s+(?=\.cOM)/gi/\s+(?=\.Com)/gi 相同,两者都与:/\s+(?=\.com)/gi &/或 /\s+(?=.COM)/gi 相同。只要设置了i 标志,“刚刚列出”的每个正则表达式都是等效的。



      就是这样! The link HERE (REGEX101) 将带您看一个示例,您可以根据需要使用正则表达式。




      我在上面提到过使用比 .com 变化更大的子字符串。

      例如,您可以使用(\s*)(?=\.\w{3,})

      这个正则表达式的问题在于,即使它匹配.txt.org.json.unclepetespurplebeet,该正则表达式也不安全。使用问题的字符串时...

      "as.asd.sd fdsfs. dfsd  d.sdfsd. sdfsdf sd   .COM"
      

      例如,您可以在LINK HERE (Regex101) 看到字符串中有 3 行。这些行表示子字符串的前瞻断言返回 true 的区域。每次断言为真时,都会产生不正确的最终匹配的可能性。虽然最后只返回了一个匹配,而且它是正确的匹配,但当在生产中运行的程序或网站中实现时,您几乎可以保证正则表达式不仅会失败,而且会继续失败得可怕,你会讨厌它。

      【讨论】:

        【解决方案5】:

        你可以试试这个。它将捕获最后一个空白段 - 在第一个捕获组中。

        (\s+)\.[^\.]*$
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-08-15
          • 1970-01-01
          • 2019-08-19
          • 2021-12-30
          • 2016-11-24
          • 1970-01-01
          • 2012-11-17
          相关资源
          最近更新 更多