如何在正则表达式中匹配“直到这个字符序列为止的任何内容”?

851

拿这个正则表达式举例:/^[^abc]/。它将匹配字符串开头的任何单个字符,但不包括abc

如果你在它后面加上一个*/^[^abc]*/ – 正则表达式将继续将每个随后的字符添加到结果中,直到遇到a, 或者b, 或者c为止。

例如,在源字符串"qwerty qwerty whatever abc hello"的情况下,该表达式将匹配到"qwerty qwerty wh"

但如果我想要匹配字符串为"qwerty qwerty whatever "呢?

换句话说,如何匹配到(但不包括)精确序列"abc"


“match but not including” 是什么意思? - Toto
9
我的意思是我想匹配 "qwerty qwerty whatever " - 不包括 "abc"。换句话说,我不想匹配结果为 "qwerty qwerty whatever abc" - callum
4
在JavaScript中,你可以使用string.split('abc')[0]来解决这个问题。虽然这不是官方解答,但我认为这种方法比正则表达式更为简单明了。 - Wylliam Judd
15个回答

1498

你没有说明正在使用哪种正则表达式,但是在任何可以被认为是“完整”的最流行的正则表达式中,这都能工作。

/.+?(?=abc)/

工作原理

.+? 部分是 .+ 的非贪婪版本(一个或多个任意字符)。当我们使用 .+ 时,引擎将基本匹配所有内容。然后,如果正则表达式中还有其他内容,它会逐步返回尝试匹配下一个部分。这是贪婪行为,意味着尽可能满足匹配条件

当使用 .+? 时,引擎不会一次性匹配所有内容并返回其他条件(如果有),而是逐步匹配下一个字符,直到匹配到正则表达式的后续部分(如果有)。这是非贪婪行为,意味着匹配最少的内容以满足要求

/.+X/  ~ "abcXabcXabcX"        /.+/  ~ "abcXabcXabcX"
          ^^^^^^^^^^^^                  ^^^^^^^^^^^^

/.+?X/ ~ "abcXabcXabcX"        /.+?/ ~ "abcXabcXabcX"
          ^^^^                          ^

接下来是 (?={contents}),一个 零宽度断言 ,也称为 向前查看 。这个组合结构匹配其内容,但不计算匹配的字符数(零宽度)。它只返回是否匹配成功或失败信息(断言)。

因此,换句话说,正则表达式/.+?(?=abc)/的意思是:

匹配任何字符,尽可能少地匹配,直到找到“abc”,但不计算“abc”在内。


32
这可能无法使用换行符正常工作,如果它们应该被捕获。 - einord
4
“.+?”和“.*”有什么区别? - robbie
9
+ 表示 1 个或更多,而 * 表示 0 个或更多。在加入/省略 ? 符号可以使其变得贪婪或非贪婪。 - jinglesthula
2
@testerjoe2 /.+?(?=abc|xyz)/ - JohnWrensby
12
我注意到如果你要查找的模式不存在,这会导致无法选择任何内容。相反,如果你使用 ^(?:(?!abc)(?!def).)*,你可以链接排除你不想要的模式,即使该模式不存在,它仍然可以抓取所有需要的内容。 - Karan Shishoo
显示剩余10条评论

215

如果你想捕获直到“abc”之前的所有内容:

/^(.*?)abc/

解释:

( ) 括号内的表达式可以通过 $1$2 等进行访问。

^ 匹配行首。

.* 匹配任何字符,? 非贪婪匹配(尽可能少地匹配所需的字符)- [1]

[1] 之所以需要这样做是因为在以下字符串中:

whatever whatever something abc something abc

默认情况下,正则表达式是 贪婪的,这意味着它会尽可能匹配更多内容。因此,/^.*abc/ 会匹配 "whatever whatever something abc something"。添加非贪婪量词 ? 可以使正则表达式只匹配 "whatever whatever something"。


10
谢谢,但是你的那个确实包含了匹配中的abc。换句话说,匹配结果为“无论什么 无论什么 某事 abc”。 - callum
2
你能解释一下你最终想要做什么吗?如果你的场景是:(A) 你想获取到"abc"之前的所有内容——只需在想要捕获的内容周围使用括号。 (B) 你想匹配到"abc"之前的字符串——你必须检查"abc",所以它需要成为正则表达式的一部分。否则你怎么能检查它是否存在呢? - Jared Ng
1
sed 似乎不支持非贪婪匹配,也不支持环视 ((?=...))。我还能做什么?例如命令:echo "ONE: two,three, FOUR FIVE, six,seven" | sed -n -r "s/^ONE: (.+?), .*/\1/p" 返回 two,three, FOUR FIVE,但我期望得到 two,three... - CodeManX
1
@CoDEmanX,你可能应该将这个问题单独发布而不是作为评论,尤其是因为它特别涉及到sed。话虽如此,为了回答你的问题:你可以看一下 这个问题 的答案。还要注意,在你的例子中,一个非贪婪的解释器会返回 two 而不是 two,three - Jared Ng
4
这是每个正则表达式答案应该看起来的方式:
  • 包括所有部分的示例和解释...
- jave.web
显示剩余6条评论

98
作为Jared Ng和@Issun指出,解决这种正则表达式问题的关键是使用“lookaround”零长度断言。在此处阅读更多信息
在您的特定情况下,可以通过正向预查来解决:.+?(?=abc) 图片胜过千言万语。请参见屏幕截图中的详细说明。

Regex101 Screenshot


61
“.+?(?=abc)”的可复制正则表达式值得更多。 - Tom
1
排除前导空格怎么样? - Royi
1
可分享的链接比截图更有价值,开个玩笑,感谢你的回答。 - Srivathsa Harish Venkataramana
"Issun" 是谁?它指的是什么答案? - Peter Mortensen
Issun的账户已经不存在了。但是他们正在参考“四处看看”——请查看我在答案中提供的链接。 - Devy

32

解决方案

/[\s\S]*?(?=abc)/

这将匹配

直到(但不包括)确切序列"abc"之前的所有内容

正如OP所请求的那样,即使源字符串包含换行符,甚至如果序列以abc开头也可以匹配。但是,如果源字符串可能包含换行符,请确保包含多行标志m

工作原理

\s表示任何空格字符(例如空格、制表符、换行符)

\S表示任何非空格字符;即与\s相反

合在一起[\s\S]表示任何字符。这与.几乎相同,除了.不匹配换行符。

*表示先前令牌的0+次出现。如果源字符串以abc开头,则我使用了这个而不是+

(?=称为正向前瞻。它要求匹配括号中的字符串,但在其之前停止,因此(?=abc)表示“直到但不包括abc,但源字符串中必须存在abc”。

?[\s\S]*(?=abc)之间表示懒惰模式(也称为非贪婪模式)。即,停止在第一个abc处。如果没有这个,如果abc出现了多次,则它将捕获每个字符直到最终出现的abc


2
运行得非常好!这应该是被接受的答案。 - GooDeeJAY

9
你需要一个前瞻断言,例如.+? (?=abc)
参见:前瞻和后顾零长度断言 请注意,[ abc ]abc不同,在方括号内它不是字符串-每个字符只是其中的一个可能性。在方括号外部,它就变成了字符串。

8

对于Java中的正则表达式,我相信在大多数正则表达式引擎中,如果您想包含最后一部分,可以使用以下方法:

.+?(abc)

例如,在这一行中:
I have this very nice senabctence

选择从“abc”之前的所有字符,包括“abc”。

使用我们的正则表达式,结果将是:我有这个非常好的senabc

测试一下:https://regex101.com/r/mX51ru/1


8

从开头匹配到"Before ABC"或"行结尾"(如果没有ABC)

(1) 如果字符串中没有ABC,则匹配整个字符串

(2) 不匹配空字符串

(未针对带有换行符的字符串进行检查)

^.+?(?=ABC|$)

7
在Python中:
对于单行情况,.+?(?=abc)有效。 [^]+?(?=abc)无效,因为Python不认识[^]作为有效的正则表达式。要使多行匹配生效,你需要使用re.DOTALL选项,例如:
re.findall('.+?(?=abc)', data, re.DOTALL)

5

因此我不得不 improvisation... 经过一段时间的努力,我成功地达到了所需的正则表达式:

Enter image description here

正如您所见,我需要在“grp-bps”文件夹之前最多一个文件夹,但不包括最后一个破折号。并且需要在“grp-bps”文件夹之后至少有一个文件夹。

适用于复制粘贴的文本版本(将“grp-bps”更改为您的文本):

.*\/grp-bps\/[^\/]+

我在寻求帮助解决问题时,找到了这个Stack Overflow的问题,但是我没有找到任何解决方案 :(


9
没有文本版本? - kiradotee

3
我想扩展sidyll的答案,以适用于正则表达式的不区分大小写版本。如果您想要匹配abc/Abc/ABC...,请使用以下正则表达式。请注意保留html标签。

sidyll的答案

如果您想要不区分大小写地匹配abc/Abc/ABC...,请使用以下正则表达式。

.+?(?=(?i)abc)

解释:

(?i) - This will make the following abc match case insensitively.

正如sidyll指出的那样,正则表达式的另一个解释保持不变。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接