突出显示的正则表达式
Regex for Highlight
当我使用不同的正则表达式来突出显示文档 (RichEditControl) 中的单词和注释时遇到问题,例如 SQL。
这是我的第一个正则表达式:
(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(--.*)
这适用于:/*blahblah*/
和 --blahblah
我还有另一个正则表达式:
((""(.|/[[:blank:]]/)*?"")|('(.|/[[:blank:]]/)*?'))
这适用于:'blahblah'
(如 sql 字符串)
但是,如果我这样做:
'/*blahblah*/'
在我写最后一个 '
程序之前显示异常:
An unhandled exception of type 'System.ArgumentException' occurred in
DevExpress.Office.v15.2.Core.dll
在此先感谢您的帮助。
这是完整代码:
private List<SyntaxHighlightToken> ParseTokens()
{
List<SyntaxHighlightToken> tokens = new List<SyntaxHighlightToken>();
DocumentRange[] ranges = null;
#region SearchSimpleCommas
Regex quotations = new Regex(@"((""(.|/[[:blank:]]/)*?"")|('(.|/[[:blank:]]/)*?'))");
ranges = document.FindAll(quotations);
foreach (var range in ranges)
{
if (!IsRangeInTokens(range, tokens))
tokens.Add(new SyntaxHighlightToken(range.Start.ToInt(), range.Length, StringSettings));
}
#endregion
#region SearchComment--/**/
Regex comment = new Regex(@"(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(--.*)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
ranges = document.FindAll(comment);
for (int i = 0; i < ranges.Length; i++)
{
tokens.Add(new SyntaxHighlightToken(ranges[i].Start.ToInt(), ranges[i].Length, CommentsSettings));
}
#endregion
tokens.Sort(new SyntaxHighlightTokenComparer());
// fill in gaps in document coverage
AddPlainTextTokens(tokens);
return tokens;
}
private void AddPlainTextTokens(List<SyntaxHighlightToken> tokens)
{
int count = tokens.Count;
if (count == 0)
{
tokens.Add(new SyntaxHighlightToken(0, document.Range.End.ToInt(), defaultSettings));
return;
}
tokens.Insert(0, new SyntaxHighlightToken(0, tokens[0].Start, defaultSettings));
for (int i = 1; i < count; i++)
{
tokens.Insert(i * 2, new SyntaxHighlightToken(tokens[i * 2 - 1].End, tokens[i * 2].Start - tokens[i * 2 - 1].End, defaultSettings));
}
tokens.Add(new SyntaxHighlightToken(tokens[count * 2 - 1].End, document.Range.End.ToInt() - tokens[count * 2 - 1].End, defaultSettings));
}
private bool IsRangeInTokens(DocumentRange range, List<SyntaxHighlightToken> tokens)
{
return tokens.Any(t => IsIntersect(range, t));
}
bool IsIntersect(DocumentRange range, SyntaxHighlightToken token)
{
int start = range.Start.ToInt();
if (start >= token.Start && start < token.End)
return true;
int end = range.End.ToInt() - 1;
if (end >= token.Start && end < token.End)
return true;
return false;
}
#region ISyntaxHighlightServiceMembers
public void ForceExecute()
{
Execute();
}
public void Execute()
{//The Exepction show in this part
document.ApplySyntaxHighlight(ParseTokens());
}
#endregion
编辑:谢谢 Harrison Mc.
我分享我使用的代码以防有人需要它,只有我修改的代码(在方法 ParseTokens 中):
#region SearchComments&Strings
Regex definitiveRegex = new Regex(@"(?<string>'[^\']*(?>\.[^\']*)*')|(?<comment>(?>/\*(?>[^*]|[\r\n]|(?>\*+(?>[^*/]|[\r\n])))*\*+/)|(?>--.*))");
MatchCollection matches = definitiveRegex.Matches(document.Text);
foreach (System.Text.RegularExpressions.Match match in matches)
{
try
{
System.Text.RegularExpressions.GroupCollection groups = match.Groups;
if (groups["string"].Value.Length > 0)
{
ranges = null;
for (int s = 0; s < groups.Count; s++)
{
if (groups[s].Value != string.Empty)
{
ranges = document.FindAll(groups[s].Value, SearchOptions.None);
for (int z = 0; z < ranges.Length; z++)
{
if(!IsRangeInTokens(ranges[z], tokens))
tokens.Add(new SyntaxHighlightToken(ranges[z].Start.ToInt(), ranges[z].Length, StringSettings));
}
}
}
}
else if (groups["comment"].Value.Length > 0)
{
ranges = null;
for (int c = 0; c < groups.Count; c++)
{
if (groups[c].Value != string.Empty)
{
ranges = document.FindAll(groups[c].Value.Trim(), SearchOptions.None);
for (int k = 0; k < ranges.Length; k++)
{
if (!IsRangeInTokens(ranges[k], tokens))
tokens.Add(new SyntaxHighlightToken(ranges[k].Start.ToInt(), ranges[k].Length, CommentsSettings));
}
}
}
}
}
catch(Exception ex){ }
}
#endregion
为了避免突出显示字符串中的注释和注释中的字符串,您需要某种"state",正则表达式无法轻松为您提供。这些情况对于单独的字符串和注释正则表达式来说很难处理,因为它需要在查找字符串时跟踪您是否在注释中,反之亦然。
"This string looks like it contains a /*comment*/ but it does not."
/* This comment looks like it contains a 'string' but it does not. */
但是,如果您使用一个具有不同组的正则表达式来匹配字符串和注释,贪婪地使用字符会阻止字符串中的 "comment" 或注释中的 "string"把事情搞砸了。
我测试了这个正则表达式,它似乎对字符串中的 "comments" 和注释中的 "strings" 都有效(都是多行)。
(?<string>'[^\']*(?>\.[^\']*)*'|""[^\""]*(?>\.[^\""]*)*"")|(?<comment>(?>/\*(?>[^*]|[\r\n]|(?>\*+(?>[^*/]|[\r\n])))*\*+/)|(?>--.*))
这里的关键是正则表达式跟踪 "state" 来确定我们是在字符串中间还是在评论中间。
要使用此功能,您需要从整体匹配中提取个别组。 (?<name>group)
语法创建一个命名组,您可以稍后将其提取。如果 <string>
组有一个匹配项,那么它就是一个字符串,如果 <comment>
组有一个匹配项,那么它就是一个注释。由于我不熟悉 document.FindAll
方法,我采用了 .NET 文档中使用 regex.Matches
方法的示例:
Regex stringAndCommentRegex = new Regex(@"(?<string>'[^\']*...");
MatchCollection matches = stringAndCommentRegex.Matches(text);
foreach (Match match in matches)
{
GroupCollection groups = match.Groups;
if (match.groups["string"].Value.Length > 0)
{
// handle string
}
else if (match.groups["comment"].Value.Length > 0)
{
// handle comment
}
}
希望这对您有所帮助!
P.S。我使用 regex101.com 来测试正则表达式,但为此我必须转义正斜杠而不是转义双引号。我尽力把它们加回来了,但我可能漏掉了一两个。
参考文献:
当我使用不同的正则表达式来突出显示文档 (RichEditControl) 中的单词和注释时遇到问题,例如 SQL。
这是我的第一个正则表达式:
(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(--.*)
这适用于:/*blahblah*/
和 --blahblah
我还有另一个正则表达式:
((""(.|/[[:blank:]]/)*?"")|('(.|/[[:blank:]]/)*?'))
这适用于:'blahblah'
(如 sql 字符串)
但是,如果我这样做:
'/*blahblah*/'
在我写最后一个 '
程序之前显示异常:
An unhandled exception of type 'System.ArgumentException' occurred in DevExpress.Office.v15.2.Core.dll
在此先感谢您的帮助。
这是完整代码:
private List<SyntaxHighlightToken> ParseTokens()
{
List<SyntaxHighlightToken> tokens = new List<SyntaxHighlightToken>();
DocumentRange[] ranges = null;
#region SearchSimpleCommas
Regex quotations = new Regex(@"((""(.|/[[:blank:]]/)*?"")|('(.|/[[:blank:]]/)*?'))");
ranges = document.FindAll(quotations);
foreach (var range in ranges)
{
if (!IsRangeInTokens(range, tokens))
tokens.Add(new SyntaxHighlightToken(range.Start.ToInt(), range.Length, StringSettings));
}
#endregion
#region SearchComment--/**/
Regex comment = new Regex(@"(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(--.*)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
ranges = document.FindAll(comment);
for (int i = 0; i < ranges.Length; i++)
{
tokens.Add(new SyntaxHighlightToken(ranges[i].Start.ToInt(), ranges[i].Length, CommentsSettings));
}
#endregion
tokens.Sort(new SyntaxHighlightTokenComparer());
// fill in gaps in document coverage
AddPlainTextTokens(tokens);
return tokens;
}
private void AddPlainTextTokens(List<SyntaxHighlightToken> tokens)
{
int count = tokens.Count;
if (count == 0)
{
tokens.Add(new SyntaxHighlightToken(0, document.Range.End.ToInt(), defaultSettings));
return;
}
tokens.Insert(0, new SyntaxHighlightToken(0, tokens[0].Start, defaultSettings));
for (int i = 1; i < count; i++)
{
tokens.Insert(i * 2, new SyntaxHighlightToken(tokens[i * 2 - 1].End, tokens[i * 2].Start - tokens[i * 2 - 1].End, defaultSettings));
}
tokens.Add(new SyntaxHighlightToken(tokens[count * 2 - 1].End, document.Range.End.ToInt() - tokens[count * 2 - 1].End, defaultSettings));
}
private bool IsRangeInTokens(DocumentRange range, List<SyntaxHighlightToken> tokens)
{
return tokens.Any(t => IsIntersect(range, t));
}
bool IsIntersect(DocumentRange range, SyntaxHighlightToken token)
{
int start = range.Start.ToInt();
if (start >= token.Start && start < token.End)
return true;
int end = range.End.ToInt() - 1;
if (end >= token.Start && end < token.End)
return true;
return false;
}
#region ISyntaxHighlightServiceMembers
public void ForceExecute()
{
Execute();
}
public void Execute()
{//The Exepction show in this part
document.ApplySyntaxHighlight(ParseTokens());
}
#endregion
编辑:谢谢 Harrison Mc.
我分享我使用的代码以防有人需要它,只有我修改的代码(在方法 ParseTokens 中):
#region SearchComments&Strings
Regex definitiveRegex = new Regex(@"(?<string>'[^\']*(?>\.[^\']*)*')|(?<comment>(?>/\*(?>[^*]|[\r\n]|(?>\*+(?>[^*/]|[\r\n])))*\*+/)|(?>--.*))");
MatchCollection matches = definitiveRegex.Matches(document.Text);
foreach (System.Text.RegularExpressions.Match match in matches)
{
try
{
System.Text.RegularExpressions.GroupCollection groups = match.Groups;
if (groups["string"].Value.Length > 0)
{
ranges = null;
for (int s = 0; s < groups.Count; s++)
{
if (groups[s].Value != string.Empty)
{
ranges = document.FindAll(groups[s].Value, SearchOptions.None);
for (int z = 0; z < ranges.Length; z++)
{
if(!IsRangeInTokens(ranges[z], tokens))
tokens.Add(new SyntaxHighlightToken(ranges[z].Start.ToInt(), ranges[z].Length, StringSettings));
}
}
}
}
else if (groups["comment"].Value.Length > 0)
{
ranges = null;
for (int c = 0; c < groups.Count; c++)
{
if (groups[c].Value != string.Empty)
{
ranges = document.FindAll(groups[c].Value.Trim(), SearchOptions.None);
for (int k = 0; k < ranges.Length; k++)
{
if (!IsRangeInTokens(ranges[k], tokens))
tokens.Add(new SyntaxHighlightToken(ranges[k].Start.ToInt(), ranges[k].Length, CommentsSettings));
}
}
}
}
}
catch(Exception ex){ }
}
#endregion
为了避免突出显示字符串中的注释和注释中的字符串,您需要某种"state",正则表达式无法轻松为您提供。这些情况对于单独的字符串和注释正则表达式来说很难处理,因为它需要在查找字符串时跟踪您是否在注释中,反之亦然。
"This string looks like it contains a /*comment*/ but it does not."
/* This comment looks like it contains a 'string' but it does not. */
但是,如果您使用一个具有不同组的正则表达式来匹配字符串和注释,贪婪地使用字符会阻止字符串中的 "comment" 或注释中的 "string"把事情搞砸了。
我测试了这个正则表达式,它似乎对字符串中的 "comments" 和注释中的 "strings" 都有效(都是多行)。
(?<string>'[^\']*(?>\.[^\']*)*'|""[^\""]*(?>\.[^\""]*)*"")|(?<comment>(?>/\*(?>[^*]|[\r\n]|(?>\*+(?>[^*/]|[\r\n])))*\*+/)|(?>--.*))
这里的关键是正则表达式跟踪 "state" 来确定我们是在字符串中间还是在评论中间。
要使用此功能,您需要从整体匹配中提取个别组。 (?<name>group)
语法创建一个命名组,您可以稍后将其提取。如果 <string>
组有一个匹配项,那么它就是一个字符串,如果 <comment>
组有一个匹配项,那么它就是一个注释。由于我不熟悉 document.FindAll
方法,我采用了 .NET 文档中使用 regex.Matches
方法的示例:
Regex stringAndCommentRegex = new Regex(@"(?<string>'[^\']*...");
MatchCollection matches = stringAndCommentRegex.Matches(text);
foreach (Match match in matches)
{
GroupCollection groups = match.Groups;
if (match.groups["string"].Value.Length > 0)
{
// handle string
}
else if (match.groups["comment"].Value.Length > 0)
{
// handle comment
}
}
希望这对您有所帮助!
P.S。我使用 regex101.com 来测试正则表达式,但为此我必须转义正斜杠而不是转义双引号。我尽力把它们加回来了,但我可能漏掉了一两个。
参考文献: