假设
匹配被定义为一个序列:
- 从
"
输入字符串中的第一个开始。让我们称之为第一次"
或开幕"
。
;
开场前没有"
;
序列中允许,如果序列中的最后一个"
是奇数,则从第一个开始计数"
。
;
不允许在序列中,如果序列中的最后一个"
是偶数,则从第一个开始计数"
。
"
以满足上述条件的最远结束。
解决方案
使用此正则表达式(原始形式)查找第一个匹配项:
^(?>[^;"]*)"((?>(?>"[^";]*(?="[^"]*$)|"[^";]*"|[^" ]*)+))"
在 C# 字符串文字中:
"^(?>[^;\"]*)\"((?>(?>\"[^\";]*(?=\"[^\"]*$)|\"[^\";]*\"|[^\"]*)+))\""
结果将在第一个捕获组中。
解释
语法解释:
(?>pattern)
是非回溯/占有子表达式。它可以防止引擎回溯。这是一种优化形式。
(?=pattern)
是零宽度正前瞻。pattern
它在不消耗文本的情况下检查前面的字符串是否符合。
|
是交替。这里我要注意的是,正则表达式引擎会从左到右检查每个规则,如果找到匹配项,则不会考虑其他规则。这意味着顺序在确定匹配时很重要。
- 我假设您熟悉其余部分,因为它们非常基础。
为了解释起见,我将使用原始正则表达式,去掉非回溯优化:
^[^;"]*"((?:"[^";]*(?="[^"]*$)|"[^";]*"|[^"]*)+)"
由于要求“一行中的第一个和最后一个引号”,每行最多有 1 个匹配项。
通过对需求进行一些分析,我们知道感兴趣部分之前的文本不应该包含;
(需求的一部分)或"
(否则,引用不会是第一个)。因此,我们可以编写^[^;"]*
以从字符串的开头锚定匹配并匹配所有内容,直到第一个引号"
。
这是引用的字符串部分,为了便于解释而分解:
"
(
(?:
"[^";]*(?="[^"]*$)
|
"[^";]*"
|
[^"]*
)+
)
"
让我们关注这3个片段,我将从下往上开始解释:
"[^";]*(?="[^"]*$)
"[^";]*"
[^"]*
对于这里的所有情况,我们遇到的最后一个报价总是一个奇怪的报价。
[^"]*
: 最后一个引号保证是奇数引号,所以我们可以有任何东西,包括;
,但除了"
.
"[^";]*"
: 最后一个引号是奇引号,后面也是奇引号。这部分处理偶数引用之后的部分,其中;
不允许。
"[^";]*(?="[^"]*$)
:这是棘手的部分,它处理字符串有奇数个引号(> = 3)的情况。我确保在偶数引号之后,没有字符串中;
的最后一个引号"
。“后跟"
字符串中的最后一个引号”是通过前瞻实现的(?="[^"]*$)
。
片段"[^";]*(?="[^"]*$)
必须放在前面"[^";]*"
以避免回溯,这样我们才能应用非回溯优化。