regex 如何按顺序匹配正则表达式命名的捕获组

zaqlnxep  于 2022-12-14  发布在  其他
关注(0)|答案(2)|浏览(150)

我在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})?)*$
编码:

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);
}

这样我就可以把用户写的所有命令都放在一个键值对列表中,然后我就可以遍历这个列表,并使用工厂方法执行每个命令。

liwlm1x9

liwlm1x91#

您可以依撷取的“索引”特性来排序撷取。请先从相关群组收集所有相符项目,然后再依撷取的“索引”来排序。

const string regEx = @"^((?<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})?)*$";
const string input = "Q I Q";

List<(string commandGroup, string command, int index)> list = new List<(string commandGroup, string command, int index)>();

// Get first match
Match match = Regex.Match(input, regEx);

// When match then get relevant groups
if (match.Success)
{
    foreach (Group group in match.Groups)
    {
        // that have captures and names which are not numbers
        if (group.Success && !int.TryParse(group.Name, out int ignore))
        {
            // Add all Captures with group name, match value and match index
            foreach(Capture capture in group.Captures)
            {
                list.Add((group.Name, capture.Value, capture.Index));
            }
        }
    }
}
else
{
    // ... throw Exception
}

// Order by Index that is original position in input
list = list.OrderBy(l => l.index).ToList();

foreach ((string commandGroup, string command, int index) in list)
{
    Console.WriteLine(commandGroup + ": " + command);
}

输出为:

quit: Q 
state: I 
quit: Q
qco9c6ql

qco9c6ql2#

当Regex返回匹配项时,您可以检查the Index property in the underlying Capture class以检查特定捕获/组在原始字符串中的哪个位置匹配。
如果您是针对单个命令流进行匹配,则如果您按此Index对生成的捕获/组进行排序,则它应按键入的顺序给予结果。
应该可以这样做:

foreach (var group in Regex
    .Match(cmdInput, "your_pattern")
    .Groups
    .Values
    .OrderBy(g => g.Index))
{
    // Use the match here
}

这并不意味着依赖Regex是解析这类复杂命令的最佳方法,但如果您想继续使用单个Regex,这可能会起作用。

相关问题