1

我正在尝试使用 c.vim 插件突出显示 Vim 中的成员变量。

例如,在

struct sockaddr_in sa;
sa.sin_family = AF_INET;

我想强调一下sin_family

所以,这是我的语法匹配代码:

syn match   cCustomMember "\(\.\)\@<=[a-zA-Z0-9_]\+\s*\((\)\@!"
hi def link cCustomMember Number

基本上我在这里想说的是,.前面必须有一个,后跟多个单词字符,可选地后跟空格,并确保后面没有括号。

但是上面突出显示正则表达式的语法似乎在 Vim 中不能正常工作。例如,如果我有这样的代码:

getWrapper()->error( NO_VALID_ID, CONNECT_FAIL.code(), CONNECT_FAIL.msg());

.msg 和 .code 被突出显示,但最后一个字母不是。但我不想突出成员函数(以圆括号结尾)

我认为这有点类似于python中的这个正则表达式问题:

a = re.compile("(?<=\.)(?:\w+)(?!\()")
print a.search(".test(").group() #produces tes, which it's desired to match nothing
print a.search(".test").group()  # produces test

如何对整个组而不是单个字母进行负前瞻。

4

1 回答 1

4

解释

您正在努力解决的问题是由于现代正则表达式引擎在查找匹配项时的基本操作方式,称为回溯。Jan Goyvaerts 在他的帖子“<a href="http://www.regexguru.com/2008/04/unintended-backtracking-can-bite-you/" rel="nofollow">意外回溯可以咬你”:

当正则表达式引擎遇到与字符串中的下一个字符不匹配的正则表达式标记时,就会发生回溯。然后,正则表达式引擎将备份到目前为止匹配的部分内容,以尝试不同的替代方案和/或重复。理解这个过程将使猜测和理解为什么正则表达式匹配它的作用和不匹配之间的所有区别。

.cod在您的情况下,正则表达式引擎将在前瞻断言匹配时回溯,测试匹配的较短组合 -并且两者都匹配.ms。下面显示了会发生什么,竖线将正则表达式已经使用的字符与字符串的其余部分分隔开来.code()

.|code()   # good start => try next char
.c|ode()   # matches => try next char
.co|de()   # matches => try next char
.cod|e()   # matches => try next char
.code|()   # whoops, next char is "(" => track back
.cod|e()   # matches => we’re done here

请注意,这仅在您使用贪婪量词时才成立,就像您在代码中所做的那样;一个惰性量词会匹配.c。请参阅关于惰性量词与贪婪量词的正则表达式教程

解决方案

绕过这个问题的明显方法是在前瞻之前禁止回溯,有效地“锁定”正则表达式已经消耗的模式部分:成员函数永远不会匹配。一些正则表达式引擎将允许您使用原子分组甚至所有格量词(它本质上是原子分组的语法糖)来做到这一点 - 更为人所知的是之前链接的页面上列出的那些。然而,Vim 的正则表达式引擎并不是其中之一

一种不太直接但更脆弱的方法是重新定义您要查找的内容:而不是匹配开头括号的否定前瞻断言,而是使用匹配所有将成员变量与其他代码(空格、逗号、分号)分隔的有效字符的肯定前瞻断言, close paren, end of line – 检查您的来源以获取更多信息) – 基本上除了一个开头的括号和另一个名称字符之外的任何东西。我会留给你把它翻译成Vim 的正则表达式语法

于 2012-08-03T14:38:22.033 回答