正则表达式查找字符串中最后一次出现的模式

Regex to find last occurrence of pattern in a string

我的字符串形式为:

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

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

到目前为止,我能够捕获空格,但不能捕获 very 最后一次使用:

\s+(?=\.\w)

如何让它不那么贪心?

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

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 个或更多 non-whitespace 个字符,然后是一个空格。

你可以这样试试:

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

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

演示:

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

你可以试试这个。它将捕获第一个捕获组中的最后一个白色 space 段。

(\s+)\.[^\.]*$
"as.asd.sd ffindMyLastOccurrencedsfs. dfindMyLastOccurrencefsd  d.sdfsd. sdfsdf sd   ..COM"

.*(?=((?<=\S)\s+)).*

replaced by `><`

>   <

作为一个更普遍的例子

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

  • 定义词findMyLastOccurrence
  • 空格(?<=\S)\s+
  • (?<=[^\.])\.+
"as.asd.sd ffindMyLastOccurrencedsfs. dfindMyLastOccurrencefsd  d.sdfsd. sdfsdf sd   ..COM"

.*(?=(findMyLastOccurrence|(?<=\S)\s+|(?<=[^\.])\.+)).*

replaced by `><`

>..<

解释:

第 1 部分 .*

  • 贪心,只要找到针头什么都找。因此,它还会捕获所有出现的针,直到最后一根针。

编辑添加:

  • 如果我们对第一个命中感兴趣,我们可以通过编写 .*?
  • 来防止贪婪

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

  • 定义贪心'find-all'的'break'条件。它由几个部分组成:
    (?=(needles))
    • positive lookahead:确保之前找到的everything之后是针 findMyLastOccurrence|(?<=\S)\s+|(?<=[^\.])\.+)|(?<=**Not**NeedlePart)NeedlePart+
    • 我们正在寻找的几根针。针本身就是图案。
    • 如果我们寻找一组空白、点或其他针刺部分,我们正在寻找的图案实际上是:任何 不是 一个针部分,后跟一个或多个针部分(因此针部分为+)。请参阅用 \S, actual dot 否定空格 \s 的示例。用 [^.]
    • 否定

第 3 部分 .*

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

常见问题的简单解决方案

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

分解一般问题


  1. 字符串

    • 普遍的问题是有一个 string 包含几个字符。
  2. SUB-STRING

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

  • before variance 是一个或多个任意字符,总是出现在 sub-string 之前。在这道题中,之前的方差是一个未知数white-space。这是一个差异,因为需要匹配的 white-space 的数量会变化(或具有动态数量)。

参考问题描述解决方案


(解决方案第 1 部分)

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

我们将从上述问题的末尾开始,往后做;我们将从 The Before Variance(或 #3)

开始

所以,如上所述,之前的方差是white-space的未知数。我们知道它包括 white-space,但我们不知道有多少,所以我们将使用 元序列用于 Any Whitespce 一个或多个量词.

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

所以我们将从...开始...

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

我还包含了 g 来告诉引擎将全局标志设置为 true。为了简洁起见,我不会解释标志,但是如果你不知道全局标志的作用,你应该去DuckDuckGo。



(解决方案第 2 部分)

请记住,我们正在反向工作,因此下一个要关注的部分是 Sub-string。在这个问题中它是.com,但作者可能希望它匹配一个具有方差的值,而不仅仅是静态字符串.com,因此我将在下面详细讨论,但是保持专注,我们现在将与 .com 合作。

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

我们要用到的Zero-Length断言叫做LOOK AHEAD,语法如下。

Look-ahead 语法:(?=Your-SubStr-Here)

我们将使用前瞻性来匹配分配给 look-ahead 的模式之前出现的方差,这将是我们的 sub-string。结果如下所示:

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

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



就是这样! The link HERE (REGEX101) 将向您展示一个示例,如果您愿意,您可以在其中使用正则表达式。




我在上面提到的 sub-string 比 .com 具有更大的方差。

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

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

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

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