Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 按组名匹配正则表达式中的组的更好方法是什么_C#_Regex_Lexer - Fatal编程技术网

C# 按组名匹配正则表达式中的组的更好方法是什么

C# 按组名匹配正则表达式中的组的更好方法是什么,c#,regex,lexer,C#,Regex,Lexer,我已经阅读并试图理解如何在正则表达式中找到匹配组的结果 我还阅读了MSDN中的所有内容 我觉得奇怪的是,C#(或.NET)似乎是正则表达式的唯一实现,它让您迭代组以查找匹配的组(特别是在需要名称的情况下),而且名称没有与组结果一起存储。例如,PHP和Python将为您提供作为正则表达式匹配结果一部分匹配的组名 我必须迭代这些组并检查是否匹配,并且我必须保留一个我自己的组名列表,因为这些名称不在结果中 下面是我要演示的代码: public class Tokenizer { private

我已经阅读并试图理解如何在正则表达式中找到匹配组的结果

我还阅读了MSDN中的所有内容

我觉得奇怪的是,C#(或.NET)似乎是正则表达式的唯一实现,它让您迭代组以查找匹配的组(特别是在需要名称的情况下),而且名称没有与组结果一起存储。例如,PHP和Python将为您提供作为正则表达式匹配结果一部分匹配的组名

我必须迭代这些组并检查是否匹配,并且我必须保留一个我自己的组名列表,因为这些名称不在结果中

下面是我要演示的代码:

public class Tokenizer
{
    private Dictionary<string, string> tokens;

    private Regex re;

    public Tokenizer()
    {
        tokens = new Dictionary<string, string>();
        tokens["NUMBER"] = @"\d+(\.\d*)?";  // Integer or decimal number
        tokens["STRING"] = @""".*""";       // String
        tokens["COMMENT"] = @";.*";         // Comment
        tokens["COMMAND"] = @"[A-Za-z]+";   // Identifiers
        tokens["NEWLINE"] = @"\n";          // Line endings
        tokens["SKIP"] = @"[ \t]";          // Skip over spaces and tabs

        List<string> token_regex = new List<string>();
        foreach (KeyValuePair<string, string> pair in tokens)
        {
            token_regex.Add(String.Format("(?<{0}>{1})", pair.Key, pair.Value));
        }
        string tok_regex = String.Join("|", token_regex);

        re = new Regex(tok_regex);
    }

    public List<Token> parse(string pSource)
    {
        List<Token> tokens = new List<Token>();

        Match get_token = re.Match(pSource);
        while (get_token.Success)
        {
            foreach (string gname in this.tokens.Keys)
            {
                Group group = get_token.Groups[gname];
                if (group.Success)
                {
                    tokens.Add(new Token(gname, get_token.Groups[gname].Value));
                    break;
                }
            }

            get_token = get_token.NextMatch();
        }
        return tokens;
    }
}
这不应该是必要的,但确实是必要的

是否仍然可以在不迭代所有组的情况下找到匹配组及其名称

编辑:比较实现。下面是我为Python实现编写的代码

class xTokenizer(object):
    """
    xTokenizer converts a text source code file into a collection of xToken objects.
    """

    TOKENS = [
        ('NUMBER',  r'\d+(\.\d*)?'),    # Integer or decimal number
        ('STRING',  r'".*"'),           # String
        ('COMMENT', r';.*'),            # Comment
        ('VAR',     r':[A-Za-z]+'),     # Variables
        ('COMMAND', r'[A-Za-z]+'),      # Identifiers
        ('OP',      r'[+*\/\-]'),       # Arithmetic operators
        ('NEWLINE', r'\n'),             # Line endings
        ('SKIP',    r'[ \t]'),          # Skip over spaces and tabs
        ('SLIST',   r'\['),             # Start a list of commands
        ('ELIST',   r'\]'),             # End a list of commands
        ('SARRAY',  r'\{'),             # Start an array
        ('EARRAY',  r'\}'),             # End end an array
    ]

    def __init__(self,tokens=None):
        """
        Constructor
            Args:
                tokens - key/pair of regular expressions used to match tokens.
        """
        if tokens is None:
            tokens = self.TOKENS
        self.tokens = tokens
        self.tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in tokens)
        pass

    def parse(self,source):
        """
        Converts the source code into a list of xToken objects.
            Args:
                sources - The source code as a string.
            Returns:
                list of xToken objects.
        """
        get_token = re.compile(self.tok_regex).match
        line = 1
        pos = line_start = 0
        mo = get_token(source)
        result = []
        while mo is not None:
            typ = mo.lastgroup
            if typ == 'NEWLINE':
                line_start = pos
                line += 1
            elif typ != 'SKIP':
                val = mo.group(typ)
                result.append(xToken(typ, val, line, mo.start()-line_start))
            pos = mo.end()
            mo = get_token(source, pos)
        if pos != len(source):
            raise xParserError('Unexpected character %r on line %d' %(source[pos], line))
        return result
类xTokenizer(对象):
"""
xTokenizer将文本源代码文件转换为xToken对象的集合。
"""
代币=[
('NUMBER',r'\d+(\.\d*)?'),#整数或十进制数
('STRING',r'.*'),#STRING
('COMMENT',r';.*),#COMMENT
('VAR',r':[A-Za-z]+'),#变量
('COMMAND',r'[A-Za-z]+'),#标识符
('OP',r'[+*\/\-]'),#算术运算符
('NEWLINE',r'\n'),#行尾
('SKIP',r'[\t]'),#跳过空格和制表符
('SLIST',r'\['),启动命令列表
('ELIST',r'\]'),结束命令列表
('SARRAY',r'\{'),启动一个数组
('array',r'\}'),#结束数组
]
定义初始化(self,令牌=None):
"""
建造师
Args:
令牌-用于匹配令牌的密钥/正则表达式对。
"""
如果令牌为无:
令牌=self.tokens
self.tokens=令牌
self.tok_regex='|'.join('(?P%s)')%pair for pair in tokens)
通过
def解析(自身,源):
"""
将源代码转换为xToken对象的列表。
Args:
源代码-源代码作为字符串。
返回:
xToken对象的列表。
"""
get_token=re.compile(self.tok_regex).match
直线=1
pos=行\u开始=0
mo=获取令牌(源)
结果=[]
虽然mo不是无:
典型=mo.lastgroup
如果typ=='NEWLINE':
行\u开始=位置
行+=1
elif-typ!='跳过“:
val=mo.group(典型)
result.append(xToken(typ,val,line,mo.start()-line_start))
pos=mo.end()
mo=获取令牌(来源,pos)
如果位置!=莱恩(资料来源):
raise xParserError('第%d行“%”上的意外字符%r(源[pos],第行))
返回结果

正如您所看到的,Python不需要迭代组,类似的事情可以在PHP中完成,我假设Java。

所有令牌类型都以不同的字符开头。如何编译一个将所有可能的起始字符映射到匹配组名的
哈希集
?这样,您只需检查整个匹配的第一个字符就可以确定匹配的是哪个组。

无需维护单独的命名组列表。改用新的

然后,您的代码将类似于以下内容:

foreach (string gname in re.GetGroupNames())
{
    Group group = get_token.Groups[gname];
    if (group.Success)
    {
        // your code
    }
}
也就是说,请注意MSDN页面上的注释:

即使捕获组没有显式命名,它们也是 自动指定的数字名称(1、2、3等)


考虑到这一点,您应该命名所有组,或者过滤掉数字组名。您可以使用一些LINQ,或者使用
的附加检查来执行此操作!Char.IsNumber(gname[0])
检查组名的第一个字符,假设任何这样的组都是无效的。或者,您也可以使用
int.TryParse
方法。

True,这优化了对匹配组的搜索,但没有回答为什么需要这样做的问题。您的问题是“是否有必要在不迭代所有组的情况下找到匹配组及其名称?”这就是我的答案。“为什么”的一个可能答案可能是Regex类不是构建lexer的最佳工具。使用其他工具有更好的方法可以做到这一点。抱歉,但我不同意,因为您的建议是使用匹配中的第一个字符作为哈希集的索引。我认为这根本不是一个好方法。这是一个没有任何迭代的O(1)方法。你有什么想法?我知道我会在将来更新正则表达式和可能的代币。因此,我不能假设第一个字符是唯一的。除了re之外,为True。GetGroupNames()包含两个额外的组。第一个名为“0”的组将被标记为(Success==true)并且是匹配组的副本,而名为“1”的组则是整个结果(Success==false)。这是为什么?我不知道。我认为.Net的另一个问题是.1用于解释数字名称。我在MSDN中找不到任何与此相关的参考。谢谢你有没有一个链接到上面写的地方?没关系,它就在这里。必须是正确的答案。在.NET中不能按名称访问组,但如果使用数字名称,则匹配中的结果组编号相同。
foreach (string gname in re.GetGroupNames())
{
    Group group = get_token.Groups[gname];
    if (group.Success)
    {
        // your code
    }
}