如何从c#中的文件路径列表中提取公共文件路径

时间:2011-12-20 15:50:33

标签: c# string

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

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

C:\ ABC \ PQR \ TMP \样品\ 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

9 个答案:

答案 0 :(得分:16)

因为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以确保文件名中的某些字母有几个相同的字母,但不包括在内。

答案 1 :(得分:3)

我认为没有使用蛮力方法进行比较,但你可以稍微优化你的解决方案:

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

答案 2 :(得分:1)

我会使用一个循环,并在每个字符串上将其与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
}

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

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

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

    }
}

答案 5 :(得分:0)

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

答案 6 :(得分:0)

保持每个细分的记录并继续前进,而所有路径都从计数开始。

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

答案 7 :(得分:0)

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

来自Enumerable.Range中的len的

(0,matchingNames.Min(s =&gt; s.Length) + 1 )。Reverse()

答案 8 :(得分:0)

对于相同的路径,例如:

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