其他教程

其他教程

Products

当前位置:首页 > 其他教程 >

正则表达式以匹配不同的分隔符

GG网络技术分享 2025-03-18 16:15 0


问题描述:

I need to get string between two delimters. And these pair of delimters are different. Here is my text:

[2018.07.10 00:30:03:640][TraceID: 8HRWSI105YVO91]->StartExecuteTask\\35

[2018.07.10 00:30:18:585][TraceID: 8707HFH7NR6307]->RequestInfo\\27

[2018.07.10 00:30:18:585][TraceID: 8707HFH7NR6307]->RequestExecuteEnd\\16

[2018.07.10 00:30:18:585][TraceID: 8707HFH7NR6307]->OutgoingData\\26651

[2018.07.10 00:31:16:773][TraceID: G8EM5LANBPC32H]->CheckUserInfo\\141

I need to get time, traceid and requset type (after -> before \\{d} )

And here is my regexp:

[\\[|\\->](.*?)[\\\\|\\]]

What i get:

2018.07.10 00:30:03:640   TraceID: 8HRWSI105YVO91  >StartExecuteTask

2018.07.10 00:30:18:585 TraceID: 8707HFH7NR6307 >RequestInfo

I can\'t remove the > from last match. Or maybe whole regexp is wrong?

网友观点:

You may use

(?:\\[|->)(.*?)[\\\\\\]]

See the regex demo

In Go, declare as

var re = regexp.MustCompile(`(?:\\[|->)(.*?)[\\\\\\]]`)

Details

  • (?:\\[|->) - a non-capturing group that matches either [ char or -> substring
  • (.*?) - Group 1: any 0+ chars, other than line break chars, as few as possible
  • [\\\\\\]] - a \\ or ] char.

Demo:

package main

import (

\\\"regexp\\\"

\\\"fmt\\\"

)

func main() {

var re = regexp.MustCompile(`(?:\\[|->)(.*?)[\\\\\\]]`)

var str = `[2018.07.10 00:30:03:640][TraceID: 8HRWSI105YVO91]->StartExecuteTask\\35`

for _, match := range re.FindAllStringSubmatch(str, -1) {

fmt.Println(match[1])

}

}

Output:

2018.07.10 00:30:03:640

TraceID: 8HRWSI105YVO91

StartExecuteTask

正则表达式“或”语录前后颠倒为啥匹配结果不同?

题主也许没有意识到,这个问题中的案例不仅仅是ab|abc会优先匹配谁的问题,它还是一个经典的、用于测试正则匹配引擎的素材。虽然有些离题,但我觉得稍微深入了解一下,还是有所启发的。

简单地说,一个正则匹配引擎有两个大方向可以选:DFA和NFA。假设我们用正则表达式「ab|abc」去匹配文本「abc」:

  • 如果只匹配到「ab」,那这个引擎是传统的NFA;这是大多数软件与编译器的选择。
  • 如果能匹配到「abc」,那基本可以判定它是DFA引擎,或者NFA的改进版本,亦或是两者的缝合怪。

尽管看上去DFA引擎做到了你想让它做的事情,但这并不意味着它比NFA引擎更加正确。实际上,DFA优势是相对快速,但功能较少;NFA则更加复杂,功能更强。

这里的核心问题在于:NFA用表达式驱动文本,它支持更多的功能,但同时它对正则表达式的设计异常敏感——表达式中一些看似无关紧要的差别,在NFA引擎中可能会得到截然不同的匹配结果。相比之下,DFA用文本驱动表达式,它不怎么关心表达式的具体形式。


DFA(Deterministic Finite Automaton,确定型有穷自动机)是“文本主导”的,它会用「目标文本」去匹配「正则表达式」。

通常来说,目标文本是很长的字符串,正则表达式则相对短小。DFA的工作过程就像拿着一根写了目标文本的长纸条,从前往后掐越来越长的一段,看它匹配正则表达式是否成功。成功的分支会保存成待定结果,继续看后面的字符是否能完成整个匹配。

这么做的好处是匹配长文本时速度很快,每一个字符都只用考虑一次。匹配花费的时间只与目标文本长度有关,正则表达式的复杂程度基本不影响。

此外,对于表达式中的多分支结构,DFA会分别记录每一个分支的匹配情况,最终选择匹配成功的最长的一个。如果最长的分支失败了,那就返回成功的第二长的,以此类推。因此,分支结构在DFA中是“贪婪”的,它会获取匹配的分支中最长的那一个。


NFA(Non-deterministic Finite Automaton,非确定型有穷自动机)则是“表达式主导”的,它用「正则表达式」去匹配「目标文本」。

每一个分支都会被放进文本中逐个字符测试,一旦成功,就把它返回作为结果。否则,取下一个分支从同样的位置开始测试。所有分支都失败了,再将文本中的“起始位置”向后推进。

这中间充满了大量的返工——某个分支中途失败了,就需要回到原始位置附近从头测试下一个分支。因此,整个匹配过程比较耗时,而且带来了陷入死循环的隐患。但同时它产生了一些十分有用的高级特性,比如回溯、忽略优先、环视等等。

题主遇到的疑惑也正源于此:NFA会依次尝试每一个分支,直到有分支匹配上。其他更长的、有潜在匹配可能的分支就被忽略了。你或许知道正则表达式中的数量词默认是“贪婪匹配”的:它会尽可能囊括更长的文本。但是,分支结构在NFA中既不“贪婪”,也不“非贪婪”,而是“顺序优先”的——靠前的分支有更多的机会被选中,即便它不是最长或最短的分支。

这种策略有它的好处。首先,许多程序设计语言在测试「A或B」的条件时,发现A为真,就不会再去测试B了。这可以提升条件表达式的执行效率,并让一些A为真时B会崩掉的表达式顺利执行(比如if number==0 or total/number<10)。而NFA处理分支结构的逻辑与此是一致的。其次,你可以用「A|B|C」来命令NFA“先测试A,再测试B,最后测试C,直到试出结果为止”,交换顺序就能改变测试的顺序,这让用户能更精确地控制正则匹配的过程。


为了支持更多特性,多数程序设计语言通常会选择实现NFA。仅在追求极致性能或快速实现,或是针对流式文本时才会考虑纯粹的DFA。

一些地方(如GNU中的awk/grep)也可能会使用两者的缝合版本——用DFA快速处理简单的表达式与分支结构(这样它就变贪婪了),把表达式中的高级功能留给NFA接手等。


回到题主的问题,我们可以大致模拟一下两种引擎是如何处理如下匹配的:

目标文本:从沈阳站到沈阳北站

正则表达式:沈阳|沈阳北

DFA引擎对它的处理过程如下:

DFA第一次匹配成功


DFA第二次匹配成功

NFA引擎的做法则有所不同:

NFA第一次匹配成功


NFA第二次匹配成功

如前所述,广泛使用的NFA引擎中,分支结构的每个分支地位并不均等,靠前的分支一旦匹配成功,靠后的分支就不再有机会了。这样的表现有时确实会产生迷惑,但以它为代价,换来的是NFA更强大的功能,以及更细致操控匹配过程的能力。

标签:

提交需求或反馈

Demand feedback