简体   繁体   English

枚举正则表达式匹配名称/值

[英]Enumerate regex match names/values

What is the C# equivalent of this pseudo-code? 该伪代码的C#等效项是什么?

var pattern = ...;
var lookup = new Dictionary<string, string>();

foreach (var group in new Regex(pattern).Matches())
{
    lookup[group.Name] = group.Value;
}

I don't see any System.Text.RegularExpressions group-related object that exposes the group name. 我没有看到任何公开组名称的与System.Text.RegularExpressions组相关的对象。

What am I missing? 我想念什么?

What I'm actually trying to do is convert a file with lines in this format: 我实际上想做的是用这种格式的行转换文件:

eventName|message|date

To an IEnumerable<EventLogLine> , with EventLogLine being: IEnumerable<EventLogLine> ,其中EventLogLine为:

public struct EventLogLine
{
    public string EventName { get; set; }
    public string Message { get; set; }
    public DateTime Date { get; set; }
}

And put those lines into a IDictionary<string /*EventName*/, IEnumerable<EventLogLine>>. 并将这些行放入IDictionary<string /*EventName*/, IEnumerable<EventLogLine>>.

To more directly answer your original question (without commenting on your approach), as I had a similar problem... 为了更直接地回答您的原始问题(不对您的方法发表评论),因为我遇到了类似的问题...

According to Mono source code , the enumeration for the Groups indexer is based on the private Match.regex field, so you'll need to still have the Regex . 根据Mono的源代码Groups索引器的枚举基于私有的Match.regex字段,因此您仍然需要Regex But if you do, like you had above... 但是,如果您这样做,就像您在上面一样...

public static Dictionary<string, string> ToDictionary(
    Regex regex, GroupCollection groups)
{
    var groupDict = new Dictionary<string, string>();
    foreach (string name in regex.GetGroupNames()){ //the only way to get the names
        Group namedGroup = groups[name]; //test for existence
        if (namedGroup.Success)
            groupDict.Add(name, namedGroup.Value);
    }
    return groupDict;
}

or, as Linq, 或者,作为Linq,

regex.GetGroupNames()
  .Where(name => groups[name].Success)
  .ToDictionary(name => name, name => groups[name].Value)

I just knocked this up in using LINQ. 我只是在使用LINQ时才提出来。 It relies on the List<string> to be filled with the lines in the file. 它依赖于List<string>用文件中的行填充。

        var lines = new List<string>();
        var dict = lines.Select(l =>
        {
            var sp = l.Split('|');
            return new EventLogLine { EventName = sp[0], Message = sp[1], Date = DateTime.Parse(sp[2]) };
        })
        .GroupBy(e => e.EventName)
        .ToDictionary(grp => grp.Key, grp => grp.AsEnumerable());

Basically you convert each line to an EventLogLine , using the Select() , then use the GroupBy() to create your grouping based on EventName, then using the ToDictionary() to run the query and create your dictionary in the format required! 基本上,您可以使用Select()将每一行转换为EventLogLine ,然后使用GroupBy()基于EventName创建分组,然后使用ToDictionary()运行查询并以所需格式创建字典!

See the example in the Match.Groups MSDN article. 请参阅Match.Groups MSDN文章中的示例 I think you should look at Alastair's answer though, seeing as your input is so simple it would probably be easier to read the code later if you just use ReadLine and Split. 我认为您应该查看Alastair的答案,因为您输入的内容非常简单,如果您仅使用ReadLine和Split,则稍后阅读代码可能会更容易。

Consider using ToLookup rather than ToDictionary . 考虑使用ToLookup而不是ToDictionary Lookups work naturally with linq and generic code in general by being immutable and by exposing aa very simple API. 通常,查找是不可变的,并且公开了一个非常简单的API,从而可以自然地与linq和通用代码一起工作。 Also, I would encapsulate the parsing into the EventLogLine struct. 另外,我会将解析封装到EventLogLine结构中。

As a result, the code would look like this: 结果,代码如下所示:

IEnumerable<string> lines;

ILookup<string, EventLogLine> lookup = 
    lines.Select(EventLogLine.Parse).ToLookup(evtLine => evtLine.EventName);

An example consumer: 消费者示例:

if(lookup["HorribleEvent"].Any())
    Console.WriteLine("OMG, Horrible!");

foreach(var evt in lookup["FixableEvent"])
    FixIt(evt);

var q = from evtName in relevantEventNames
        from evt in lookup[evtName]
        select MyProjection(evt);

Note that you do not need to check for key-existance, unlike for a Dictionary: 请注意,与字典不同,您不需要检查密钥是否存在:

if(dictionary.ContainsKey("HorribleEvent")) //&& dictionary["HorribleEvent"].Any() sometimes needed
    Console.WriteLine("OMG, Horrible!");

if(dictionary.ContainsKey("FixableEvent"))
    foreach(var evt in lookup["FixableEvent"])
        FixIt(evt);

var q = from evtName in relevantEventNames.Where(dictionary.ContainsKey)
        from evt in dictionary[evtName]
        select MyProjection(evt);

As you may notice, working with a dictionary containing IEnumerable values introduces subtle friction - ILookup is what you want! 您可能会注意到,使用包含IEnumerable值的字典会产生细微的摩擦-ILookup是您想要的!

Finally, the modified EventLogLine : 最后,修改后的EventLogLine

public struct EventLogLine {
    public string EventName { get; private set; }
    public string Message { get; private set; }
    public DateTime Date { get; private set; }

    public static EventLogLine Parse(string line) {
        var splitline = line.Split('|');
        if(splitline.Length != 3) throw new ArgumentException("Invalid event log line");
        return new EventLogLine { 
            EventName = splitline[0],
            Message = splitline[1],
            Date = DateTime.Parse(splitline[2]),
        };
    }
}

To answer this part of your question: 要回答问题的这一部分:

I don't see any System.Text.RegularExpressions group-related object that exposes the group name. 我没有看到任何公开组名称的与System.Text.RegularExpressions组相关的对象。 What am I missing? 我想念什么?

I have adapted Eamon Nerbonne's struct to use regular expressions: 我已经改编了Eamon Nerbonne的结构以使用正则表达式:

public struct EventLogLine
{
    public string EventName { get; private set; }
    public string Message { get; private set; }
    public DateTime Date { get; private set; }

    private static Regex expectedLineFormat = new Regex(
            @"^(?<eventName>[^|]*)\|(?<message>[^|]*)\|(?<date>[^|]*)$",
            RegexOptions.Singleline | RegexOptions.Compiled
    );

    public static EventLogLine Parse(string line) {

        Match match = expectedLineFormat.Match(line);

        if (match.Success) {
            return new EventLogLine {
                EventName = match.Groups["eventName"].ToString(),
                Message = match.Groups["message"].ToString(),
                Date = DateTime.Parse(match.Groups["date"].ToString()
            };
        }
        else {
            throw new ArgumentException("Invalid event log line");
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM