【问题标题】:How to determine if any element in a collection appears in a string with LINQ?如何确定集合中的任何元素是否出现在使用 LINQ 的字符串中?
【发布时间】:2017-11-01 15:41:09
【问题描述】:

我有Dictionary<string, List<string>> 对象。 Key 代表文件名,ValueList<string>,代表文件中某些方法的名称。

我遍历字典并使用Key 从文件中读取数据。然后我试图在这个文件中找到包含来自Values 对象的元素的行:

static void FindInvalidAttributes(Dictionary<string, List<string>> dictionary)
{
    //Get the files from my controller dir
    List<string> controllers = Directory.GetFiles(controllerPath, "*.cs", SearchOption.AllDirectories).ToList<string>();

    //Iterate over my dictionary
    foreach (KeyValuePair<string, List<string>> entry in dictionary)
    {
        //Build the correct file name using the dictionary key
        string controller = Path.Combine(ControllerPath, entry.Key + "Controller.cs");

        if (File.Exists(controller))
        {
            //Read the file content and loop over it
            string[] lines = File.ReadAllLines(controller);
            for (int i = 0; i < lines.Count(); i++)
            {
                //loop over every element in my dictionary's value (List<string>)
                foreach (string method in entry.Value)
                {
                    //If the line in the file contains a dictionary value element
                    if (lines[i].IndexOf(method) > -1 && lines[i].IndexOf("public") > -1)
                    {
                        //Get the previous line containing the attribute
                        string verb = lines[i - 1];
                    }
                }
            }
        }
    }
}

必须有一种更简洁的方式来实现if (File.Exists(controller)) 语句中的代码。我不想将foreach 嵌套在for 内部,最父级的foreach 内部。

问题:如何使用 LINQ 确定 string 是否包含 List&lt;string&gt; 中的任何元素?

请注意,这两个值不相同;字符串的一部分应该包含整个列表元素。我能够找到大量示例来查找列表元素中的字符串,但这不是我想要做的。

示例:

lines[0] = "public void SomeMethod()";
lines[1] = "public void SomeOtherMethod()";

List<string> myList = new List<string>();
myList.Add("SomeMethod");
myList.Add("AnotherMethod");

使用上面的数据,lines[0] 应该会导致我的FindInvalidAttributes 方法查看上一行,因为该字符串包含myList 中的第一个元素。 lines[1] 应该导致方法检查上一行,因为 SomeOtherMethod 没有出现在 myList 中。

编辑我很好奇为什么这被否决并标记为“过于宽泛”而被关闭。我提出了一个非常具体的问题,提供了我的代码、示例数据和示例数据的预期输出。

【问题讨论】:

  • 你不能对这个任务使用反射吗?
  • 方法定义可以分成不同的行,如果程序员决定这样做会破坏你的逻辑。重载的方法也可能把事情搞砸。获取方法属性(相对于类属性)的示例是here;使用反射获取方法列表作为MethodInfo 对象,然后迭代这些对象以获取每个方法的属性。解析.cs文件我敢肯定是个大黄蜂巢。
  • @Quantic 就我的目的而言,我可以保证方法声明在一行上,并且不需要担心重载。在我的所有控制器中定义的每个 public 方法都标有 2 个可能属性中的 1 个。一些 JS 文件奇怪地以非常规的方式调用这些方法,并且需要确保这些方法具有正确的属性分配。我会考虑反思,但我更愿意解决“如何确定字符串是否包含集合中的元素”的一般问题,而不是“此方法具有哪些属性”的具体问题。

标签: c# linq


【解决方案1】:

如果您有 lines 包含文件中的所有行,entry.Value 作为要查找的单词列表,那么以下将返回包含至少一个来自 entry.Value 的单词的所有行:

var result = lines.Where(l => l.Split(' ').Intersect(entry.Value).Any());

或更多进入您的具体示例:

var result = 
    lines.Where(l => 
        l.Trim().StartsWith("public") && entry.Value.Any(l.Contains));

【讨论】:

  • dotnetfiddle.net/qC8hP0 - FindInvalidAttributes 是我的实现。 FindWithLINQ 是你的。第一个正确识别 lines 中的某些内容与 myList 中的元素匹配,第二个则没有。我尝试了您提供的两个 sn-ps 代码,但都没有生成任何输出
  • 你是对的,因为括号。我已经编辑了我的答案
  • 为了确保我阅读正确,entry.Value.Any(l.Contains) 基本上可以读作“如果行 (l) 包含在 entry.Value 中找到的任何值”,对吗?
  • 是的。就是这个意思
  • @sab669 这是string.Contains() method,而不是例如IndexOf,并在该页面上解释了注意事项。
【解决方案2】:

您可以构建一个正则表达式,一次性查找文件列表中的所有项目。

另外,请注意,您根本没有使用 controllers 变量。

static void FindInvalidAttributes(Dictionary<string, List<string>> dictionary)
{
    //Get the files from my controller dir
    List<string> controllers = Directory.GetFiles(controllerPath, "*.cs", SearchOption.AllDirectories).ToList<string>();

    //Iterate over my dictionary
    foreach (var entry in dictionary)
    {
        //Build the correct file name using the dictionary key
        string controller = Path.Combine(ControllerPath, entry.Key + "Controller.cs");

        if (File.Exists(controller))
        {
            var regexText = "(?<attribLine>.*)\n" + string.Join("|", entry.Value.Select(t => "[^\n]*public\s[^\n]*" + Regex.Escape(t)))
            var regex = new Regex(regexText)
            //Read the file content and loop over it
            var fileContent = File.ReadAllText(controller);
            foreach (Match match in regex.Matches(fileContent))
            {
                 // Here, match.Groups["attribLine"] should contain here what you're looking for.
            }
        }
    }
}

我没有输入,因此无法轻松测试代码或正则表达式,但它应该给出方法和简化的总体思路。

【讨论】:

  • 糟糕,我在处理这个问题时忘记删除controllers 的声明。你能解释一下var regexText = ... 行在做什么吗?我不明白 (?&lt;attribLine&gt;.*)\n 并将其放入 regexr.com 表示 ? 是量词的目标。
  • @sab669 regexText 是一个正则表达式,它尝试匹配一行,该行后面跟着包含列表中至少一个字符串的下一行。第一行被捕获在一个命名组中 - attribLine - 在匹配时可以检索。该行包含您的方法的属性信息。有意义吗?
  • 啊,我不熟悉命名组。所以基本上这只是在这个match.Groups 集合中创建元素的一种方法,它可以被字符串引用。但是正则表达式文本本身存在一些错误:我在"[^\n]*public\s[^\n]\*"s 上得到Unrecognized escape sequence,您可以看到here(尽管直到我真正将它放入VS它告诉我它是s 特别)
  • OK 通过在字符串前面加上 @(并添加缺少的分号)来解决该问题。 Updated dotnetfiddle 好像不错?虽然match.Groups["attribLine"]似乎没有编译
  • @sab669,我看到了编译问题。将foreach (var match 更改为foreach (Match match 。我刚刚在你的小提琴中尝试过,它奏效了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-10
  • 2016-06-18
  • 1970-01-01
  • 2012-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多