【问题标题】:how to extract common file path from list of file paths in c#如何从c#中的文件路径列表中提取公共文件路径
【发布时间】:2011-12-20 15:50:32
【问题描述】:

从c#中的文件路径字符串列表中提取公共文件路径的最佳方法是什么?

例如: 我在 List 变量中列出了 5 个文件路径,如下所示

c:\abc\pqr\tmp\sample\b.txt
c:\abc\pqr\tmp\new2\c1.txt
c:\abc\pqr\tmp\b2.txt
c:\abc\pqr\tmp\b3.txt
c:\abc\pqr\tmp\tmp2\b2.txt

输出应该是 c:\abc\pqr\tmp

【问题讨论】:

  • 暴力破解方法是不是因为某种原因没用?
  • 看看这个SO post,它以多种不同的方式解决了同样的问题。为了方便谷歌搜索,这是“最长公共子字符串”问题。
  • 您是否还需要考虑相对和非规范化(即 C:\abc\..\abc\pqr\tmp)、映射(SUBST)或链接路径?
  • 前缀是这里的关键词。试试这个:stackoverflow.com/a/2070434/862973希望这会有所帮助。

标签: c# string


【解决方案1】:

因为使用 LINQ* 可以最好地解决所有问题:
*使用 LINQ 可以最好地解决所有问题。

using System.Collections.Generic;
using System.IO;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        List<string> Files = new List<string>()
        {
            @"c:\abc\pqr\tmp\sample\b.txt",
            @"c:\abc\pqr\tmp\new2\c1.txt",
            @"c:\abc\pqr\tmp\b2.txt",
            @"c:\abc\pqr\tmp\b3.txt",
            @"c:\a.txt"
        };

        var MatchingChars =
            from len in Enumerable.Range(0, Files.Min(s => s.Length)).Reverse()
            let possibleMatch = Files.First().Substring(0, len)
            where Files.All(f => f.StartsWith(possibleMatch))
            select possibleMatch;

        var LongestDir = Path.GetDirectoryName(MatchingChars.First());
    }
}

说明:

第一行获取要评估的可能匹配的长度列表。我们首先想要最长的可能性(所以我将枚举反转为 0、1、2、3;将其变为 3、2、1、0)。

然后我得到要匹配的字符串,它只是给定长度的第一个条目的子字符串。

然后我过滤结果,以确保我们只包含所有文件开头的可能匹配项。

最后,我返回第一个结果,这将是最长的子字符串并调用 path.getdirectoryname 以确保文件名中是否有一些相同的字母不包含在内。

【讨论】:

  • 我喜欢顶部的评论! :)
  • 您能否添加对 LINQ 代码中 3 行中每一行的作用的描述?特别是在第一行对Reverse() 的调用让我有点困惑。
  • 如果有两条路径 c:\oog\pong 和 c:\oog\pongle 这将返回 c:\oog\pong,它应该只返回 c:\oog\。它需要逐个目录而不是逐个字符地检查。
  • 聪明的主意。解决上述问题应该不难。
  • @mongus,这就是路径调用的行。
【解决方案2】:

我认为使用蛮力方法进行比较没有什么“技巧”,但您可以稍微优化您的解决方案:

Prepare:
Find the shortest string

Repeat:
See if all of the other strings contain it
If so, you're done
If not, remove one or more characters

【讨论】:

  • C:\WorldsLongestFileNameIsntTheShortestStringNecessarilyButItIsAtTheRoot.Wrong .. 但如果您将其缩短一个字符并重试直到它消失,则不会这样。哦。
【解决方案3】:

我会使用一个循环,并在每个字符串上用s.Split('\') 分割它。

然后它遍历第一个元素和下一个元素,边走边保存。

当我找到一个不同的,我可以返回最后一次迭代的结果。

string commonPath(string[] paths) {
    // this is a Java notation, I hope it's right in C# as well? Let me know!
    string[][] tokens = new string[paths.length][];

    for(int i = 0; i < paths.Length; i++) {
        tokens[i] = paths.Split('\\');
    }

    string path = "";

    for(int i = 0; i < tokens[0].Length; i++) {
        string current = tokens[0][i];
        for(int j = 1; j < tokens.Length; j++) {
            if(j >= tokens[i].Length) return path;
            if(current != tokens[i][j]) return path;
        }
        path = path + current + '\\';
    }
    return path; // shouldn't reach here, but possible on corner cases
}

【讨论】:

    【解决方案4】:

    这是一个快速实现,它只是循环遍历字符串列表,然后比较从原始字符串修剪的字符串开头的字符:

    List<string> list1 = new List<string>();
    list1.Add(@"c:\abc\pqr\tmp\sample\b.txt");
    list1.Add(@"c:\abc\pqr\tmp\new2\c1.txt");
    list1.Add(@"c:\abc\pqr\tmp\b2.txt");
    list1.Add(@"c:\abc\pqr\tmp\b3.txt");
    list1.Add(@"c:\abc\pqr\tmp\tmp2\b2.txt");
    
    string baseDir = "";
    foreach (var item in list1)
    {
        if (baseDir == "")
            baseDir = System.IO.Path.GetDirectoryName(item);
        else
        {
            int index = 0;
            string nextDir = System.IO.Path.GetDirectoryName(item);
            while (index< baseDir.Length && index<nextDir.Length && 
                baseDir[index] == nextDir[index])
            {
                index++;
            }
            baseDir = baseDir.Substring(0, index);
        }
    }
    MessageBox.Show(baseDir);
    

    【讨论】:

    • 该死,我只花了大约 5 分钟的时间编码、评论和测试同样的东西,却发现你在这里......仍然 +1,因为,你知道,同样的想法 :)
    • 这个实现有一个缺点。如果你有这个来源c:\abc\pqr\tmp; c:\abc\pqr\tmb; c:\abc\pqr\tmc那么公共路径就像c:\abc\pqr\tm
    【解决方案5】:

    使用第一个路径作为迭代器种子:

    using System;
    using System.Collections.Generic;
    using System.IO;
    
    namespace stackoverflow1
    {
        class MainClass
        {
            public static void Main (string[] args)
            {
                List<String> paths=new List<String>();
                paths.Add(@"c:\abc\pqr\tmp\sample\b.txt");
                paths.Add(@"c:\abc\pqr\tmp\new2\c1.txt");
                paths.Add(@"c:\abc\pqr\tmp\b2.txt");
                paths.Add(@"c:\abc\pqr\tmp\b3.txt");
                paths.Add(@"c:\abc\pqr\tmp\tmp2\b2.txt");
    
                Console.WriteLine("Found: "+ShortestCommonPath(paths));
    
            }
    
            private static String ShortestCommonPath(IList<String> list)
            {
                switch (list.Count)
                {
                case 0: return null;
                case 1: return list[0];
                default:
                    String s=list[0];
                    while (s.Length>0)
                    {
                        bool ok=true;
                        for (int i=1;i<list.Count;i++)
                        {
                            if (!list[i].StartsWith(s))
                            {
                                ok=false;
                                int p=s.LastIndexOf(Path.DirectorySeparatorChar);
                                if (p<0) return "";
                                s=s.Substring(0,p);
                                break;
                            }
                        }
                        if (ok) break;
                    }
                    return s;
                }
            }
    
        }
    }
    

    【讨论】:

      【解决方案6】:

      您可以将路径分解成段(即用反斜杠分割),然后一次构建一个段并比较结果,直到找到匹配的结尾。我怀疑这是最好的方法,但它会起作用。

      【讨论】:

        【解决方案7】:

        记录每个路段并继续前进,同时所有路径都以该记录开始。

        void Main()
        {
            string[] paths = new[] { @"c:\abc\pqr\tmp\sample\b.txt",
                                    @"c:\abc\pqr\tmp\new2\c1.txt",
                                    @"c:\abc\pqr\tmp\b2.txt",
                                    @"c:\abc\pqr\tmp\b3.txt",
                                    @"c:\abc\pqr\tmp\tmp2\b2.txt"};
        
            var test = new List<string>();
            var common = paths[0].Split('\\').TakeWhile ( segment => 
            {
                test.Add ( segment );
                return paths.All ( path => path.StartsWith ( String.Join ("\\", test )  + "\\") ) ;
            } );
        
            Console.WriteLine ( String.Join ("\\", common ) );
        }
        

        【讨论】:

          【解决方案8】:

          如果路径之一正是应返回的目录名称,则所选解决方案无法正常工作。我认为应该有:

          from len in Enumerable.Range(0, matchingNames.Min(s => s.Length) + 1).Reverse()

          【讨论】:

            【解决方案9】:

            对于相同的路径,最佳答案失败,例如:

            string str1 = @"c:\dir\dir1\dir2\dir3";
            string str2 = @"c:\dir\dir1\dir2\dir3";
            

            这样更好:Find common prefix of strings

            然而

            .TakeWhile(s => s.All(d => d == s.First()))
            

            应该是

            .TakeWhile(s =>
                  {
                      var reference = s.First();
                      return s.All(d => string.Equals(reference, d, StringComparison.OrdinalIgnoreCase));
                  })
            

            【讨论】:

              猜你喜欢
              • 2013-06-08
              • 2011-09-01
              • 2010-10-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多