You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何按输入顺序匹配正则表达式命名捕获组?

问题

我在C#控制台应用中使用正则表达式捕获命令,支持用户在同一行输入多个命令,但目前捕获的命令顺序是正则命名捕获组的定义顺序,而非用户实际输入顺序。例如用户输入Q I,当前正则会先捕获I再捕获Q,导致命令执行顺序错误。能否仅通过调整正则表达式修复该问题?

当前使用的正则表达式:

^((?<state>I\s?)?()|(?<quit>Q\s?)?()|(?<time>VR (\d{2}\.\d{2}\.\d{4}\. \d{2}\:\d{2}\:\d{2})\s?)?()|(?<connection_type>V (?:PU|PO|OS) (:?S|Z) (\d{2}\.\d{2}\.\d{4}\. \d{2}\:\d{2}\:\d{2}) (\d{2}\.\d{2}\.\d{4}\. \d{2}\:\d{2}\:\d{2})\s?)?()|(?<request_file>UR [0-9a-zA-Z_-]{1,}\.csv\s?)?()|(?<create_reserved_request>ZD [0-9]{1,}\s?)?()|(?<create_request>ZP [0-9]{1,} [0-9]{1,}\s?)?()|(?<ship_channel>F [0-9]{1,} [0-9]{1,}( [Q])?\s?)?()|(?<table_formating>T(( P)?()|( Z)?()|( RB)?\s?){1,3})?()|(?<occupied_connections_by_type>ZA (\d{2}\.\d{2}\.\d{4}\. \d{2}\:\d{2})\s?)?()|(?<print_data>VF(( R)?()|( B)?()|( M)?()|( K)?()|( D)?\s?){1,8})?)*$

对应的C#代码:

string regexCommands = @"(^((?<status_vezova>I\s?)?()|(?<prekid_rada>Q\s?)?()|(?<vrijeme>VR (\d{2}\.\d{2}\.\d{4}\. \d{2}\:\d{2}\:\d{2})\s?)?()" +                
                        @"|(?<vezovi_po_vrsti>V (?:PU|PO|OS) (:?S|Z) (\d{2}\.\d{2}\.\d{4}\. \d{2}\:\d{2}\:\d{2}) (\d{2}\.\d{2}\.\d{4}\. \d{2}\:\d{2}\:\d{2})\s?)?()|" +
                        @"(?<datoteka_zahtjeva>UR [0-9a-zA-Z_-]{1,}\.csv\s?)?()|(?<kreiranje_rezerviranog_zahtjev>ZD [0-9]{1,}\s?)?()|" +
                        @"(?<kreiranje_zahtjeva>ZP [0-9]{1,} [0-9]{1,}\s?)?()|(?<komunikacija_brod_kanal>F [0-9]{1,} [0-9]{1,}( [Q])?\s?)?()|" +
                        @"(?<format_ispisa_tablica>T(( P)?()|( Z)?()|( RB)?\s?){1,3})?()|(?<zauzeti_vezovi_prema_vrsti>ZA (\d{2}\.\d{2}\.\d{4}\. \d{2}\:\d{2})\s?)?()|(?<ispis_podataka>VF(( R)?()|( B)?()|( M)?()|( K)?()|( D)?\s?){1,8})?)*$)";

string commands = Console.ReadLine();
Regex regex = new Regex(regexCommands);
Match match = regex.Match(commands);
if (!regex.IsMatch(naredba!))
{
    throw new Exception();
}

List<KeyValuePair<string, string>> listRegexGroupsAndValues = new List<KeyValuePair<string, string>>();
GroupCollection groups = match.Groups;

//insert all regex named groups and values in list
foreach (string groupName in regex.GetGroupNames())
{
    if (groupName.Length > 2)
        listRegexGroupsAndValues.Add(new KeyValuePair<string, string>(groupName, groups[groupName].Value));
}

//print
foreach (KeyValuePair<string, string> pair in listRegexGroupsAndValues)
{
     Console.WriteLine(pair.Key + " " + pair.Value);
}

目前我会把用户输入的所有命令存入键值对列表,之后通过工厂方法迭代执行每个命令。


解决方案

要保留用户输入的命令顺序,不能依赖命名捕获组的遍历(组顺序由正则定义决定),需调整正则让其按输入顺序捕获每个命令实例,同时标记命令类型。

1. 修改正则表达式,按输入顺序捕获命令

将原有的多个独立命名组,改为可重复的统一捕获结构,每个匹配项同时记录命令类型和完整内容,核心思路是用同一个命名组重复捕获所有命令实例:

调整后的正则(修正了原正则的语法错误,并优化匹配逻辑):

^(?:(?<command>(?<type>I)\s?)|(?<command>(?<type>Q)\s?)|(?<command>(?<type>VR) (\d{2}\.\d{2}\.\d{4}\. \d{2}:\d{2}:\d{2})\s?)|(?<command>(?<type>V) (?:PU|PO|OS) [SZ] (\d{2}\.\d{2}\.\d{4}\. \d{2}:\d{2}:\d{2}) (\d{2}\.\d{2}\.\d{4}\. \d{2}:\d{2}:\d{2})\s?)|(?<command>(?<type>UR) [0-9a-zA-Z_-]+\.csv\s?)|(?<command>(?<type>ZD) \d+\s?)|(?<command>(?<type>ZP) \d+ \d+\s?)|(?<command>(?<type>F) \d+ \d+( Q)?\s?)|(?<command>(?<type>T)(?: P| Z| RB){1,3}\s?)|(?<command>(?<type>ZA) (\d{2}\.\d{2}\.\d{4}\. \d{2}:\d{2})\s?)|(?<command>(?<type>VF)(?: R| B| M| K| D){1,8}\s?))*$

关键改动:

  • 用非捕获组(?:...)包裹所有命令分支,整体重复*匹配多个命令
  • 每个分支内同时用<command>捕获完整命令文本,<type>捕获命令标识(如I、Q)
  • 修正原正则中(:?的语法错误为标准非捕获组(?:

2. 修改C#代码,按匹配顺序提取命令

原代码遍历所有命名组,现在需要读取<command>和<type>组的Captures集合——重复捕获的组会在该集合中保存所有匹配项,顺序严格对应用户输入顺序:

调整后的核心代码:

string regexCommands = @"^(?:(?<command>(?<type>I)\s?)|(?<command>(?<type>Q)\s?)|(?<command>(?<type>VR) (\d{2}\.\d{2}\.\d{4}\. \d{2}:\d{2}:\d{2})\s?)|(?<command>(?<type>V) (?:PU|PO|OS) [SZ] (\d{2}\.\d{2}\.\d{4}\. \d{2}:\d{2}:\d{2}) (\d{2}\.\d{2}\.\d{4}\. \d{2}:\d{2}:\d{2})\s?)|(?<command>(?<type>UR) [0-9a-zA-Z_-]+\.csv\s?)|(?<command>(?<type>ZD) \d+\s?)|(?<command>(?<type>ZP) \d+ \d+\s?)|(?<command>(?<type>F) \d+ \d+( Q)?\s?)|(?<command>(?<type>T)(?: P| Z| RB){1,3}\s?)|(?<command>(?<type>ZA) (\d{2}\.\d{2}\.\d{4}\. \d{2}:\d{2})\s?)|(?<command>(?<type>VF)(?: R| B| M| K| D){1,8}\s?))*$";

string commands = Console.ReadLine();
Regex regex = new Regex(regexCommands);
Match match = regex.Match(commands);
if (!match.Success)
{
    throw new Exception("无效的命令格式");
}

// 按输入顺序提取命令和对应组名
List<KeyValuePair<string, string>> orderedCommands = new List<KeyValuePair<string, string>>();
CaptureCollection typeCaptures = match.Groups["type"].Captures;
CaptureCollection commandCaptures = match.Groups["command"].Captures;

for (int i = 0; i < typeCaptures.Count; i++)
{
    string type = typeCaptures[i].Value;
    string commandText = commandCaptures[i].Value.Trim();
    // 将命令类型映射到原有的组名,保持与现有工厂方法的兼容性
    string groupName = type switch
    {
        "I" => "status_vezova",
        "Q" => "prekid_rada",
        "VR" => "vrijeme",
        "V" => "vezovi_po_vrsti",
        "UR" => "datoteka_zahtjeva",
        "ZD" => "kreiranje_rezerviranog_zahtjev",
        "ZP" => "kreiranje_zahtjeva",
        "F" => "komunikacija_brod_kanal",
        "T" => "format_ispisa_tablica",
        "ZA" => "zauzeti_vezovi_prema_vrsti",
        "VF" => "ispis_podataka",
        _ => ""
    };
    if (!string.IsNullOrEmpty(groupName))
    {
        orderedCommands.Add(new KeyValuePair<string, string>(groupName, commandText));
    }
}

// 按输入顺序执行/打印命令
foreach (var pair in orderedCommands)
{
    Console.WriteLine($"{pair.Key} {pair.Value}");
}

核心原理说明

原正则的问题在于:多个独立命名组是并行匹配的,每个组仅保留最后一次匹配结果,且遍历组的顺序由正则定义决定,与用户输入顺序无关。调整后的正则通过重复捕获同一组,让Captures集合保存所有匹配实例,顺序严格对应用户输入的命令顺序,从根源上解决了执行顺序错误的问题。

内容的提问来源于stack exchange,提问作者S4kii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 10:01:47