我正在尝试捕获VBA评论.到目前为止,我有以下内容
'[^";]+\Z
Run Code Online (Sandbox Code Playgroud)
它捕获任何以单引号开头但在字符串结尾之前不包含任何双引号的内容.即它不匹配双引号字符串中的单引号.
dim s as string ' a string variable -- works
s = "the cat's hat" ' quote within string -- works
Run Code Online (Sandbox Code Playgroud)
但如果注释包含双引号字符串,则会失败
即
dim s as string ' string should be set to "ten"
Run Code Online (Sandbox Code Playgroud)
我怎样才能修复我的正则表达式呢?
@Jeff Wurz的评论(^\'[^\r\n]+$|''[^\r\n]+$)中的模式甚至不匹配任何测试样本,并且链接的问题是无用的,那里的正则表达式只会匹配OP问题中的特定注释,而不是"VBA注释语法".
你提出的正则表达式比我放弃正则表达式时的效果更好.
做得好!
问题是您无法使用正则表达式解析VBA注释.
在Lexers vs Parsers中,@ SasQ的答案很好地解释了乔姆斯基的语法水平:
3级:常规语法
它们使用正则表达式,也就是说,它们只能由字母表符号(a,b),它们的连接符号(ab,aba,bbb etd.)或替代符号(例如a | b)组成.它们可以实现为有限状态自动机(FSA),如NFA(非确定性有限自动机)或更好的DFA(确定性有限自动机).常规语法无法处理嵌套语法,例如正确嵌套/匹配的括号(()()(()())),嵌套的HTML/BBcode标签,嵌套块等等.因为处理它的状态自动机应该必须有无限多个状态来处理无限多的嵌套级别.
第2级:无上下文语法
它们可以在语法树中具有嵌套的,递归的,自相似的分支,因此它们可以很好地处理嵌套结构.它们可以实现为具有堆栈的状态自动机.此堆栈用于表示语法的嵌套级别.实际上,它们通常被实现为自上而下的递归下降解析器,它使用机器的过程调用栈来跟踪嵌套级别,并在语法中对每个非终端符号使用递归调用的过程/函数.但他们无法处理上下文相关的语法.例如,当你有一个表达式x + 3并且在一个上下文中,这个x可以是变量的名称,而在其他上下文中它可以是函数的名称等.
第1级:上下文敏感的语法
正则表达式根本不是解决此问题的合适工具,因为只要有多个引号(/撇号)或涉及双引号时,您需要弄清楚代码行中最左侧的撇号是否为在双引号内,如果是,那么你需要匹配双引号并在结束双引号后找到最左边的撇号 - 实际上,最左边的撇号不是字符串文字的一部分,是你的评论标记.
我的理解是VBA注释语法是一个上下文敏感的语法(级别1),因为撇号只是你的标记,如果它不是字符串文字的一部分,并且要弄清楚撇号是否是字符串文字的一部分,最简单可能是从左到右走你的字符串并在IsInsideQuote你遇到双引号时切换一些标志...但只有当它们没有被转义(加倍)时.实际上你甚至没有检查字符串文字中是否有撇号:你只是一直走,直到打开引号关闭,只有当你在"引号内标志"中False找到一个评论标记时才会遇到单引号.
祝好运!
这是您缺少的测试用例:
s = "abc'def ""xyz""'nutz!" 'string with apostrophes and escaped double quotes
Run Code Online (Sandbox Code Playgroud)
如果你不关心捕获字符串文字,你可以简单地忽略转义的双引号,并在这里看到3个字符串文字:"abc'def ","xyz"和"'nutz!".
这个C#代码输出'string with apostrophes and escaped double quotes(所有字符串内双引号都在代码中使用反斜杠进行转义),并使用我给它的所有测试字符串:
static void Main(string[] args)
{
var instruction = "s = \"abc'def \"\"xyz\"\"'nutz!\" 'string with apostrophes and escaped double quotes";
// var instruction = "s = \"the cat's hat\" ' quote within string -- works";
// var instruction = "dim s as string ' string should be set to \"ten\"";
int? commentStart = null;
var isInsideQuotes = false;
for (var i = 0; i < instruction.Length; i++)
{
if (instruction[i] == '"')
{
isInsideQuotes = !isInsideQuotes;
}
if (!isInsideQuotes && instruction[i] == '\'')
{
commentStart = i;
break;
}
}
if (commentStart.HasValue)
{
Console.WriteLine(instruction.Substring(commentStart.Value));
}
Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)
然后,如果要捕获所有合法注释,则需要处理legacy Rem关键字,并考虑行继续:
Rem this is a legal comment
' this _
is also _
a legal comment
Run Code Online (Sandbox Code Playgroud)
换句话说,\r\n本身并不足以正确识别所有语句结束标记.
一个合适的词法分析器+解析器似乎是捕获所有注释的唯一方法.