突出显示的正则表达式

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 来测试正则表达式,但为此我必须转义正斜杠而不是转义双引号。我尽力把它们加回来了,但我可能漏掉了一两个。

参考文献: