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

从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目录\ TMP 2 \ b2.txt

输出应为c:\ abc \ pqr \ tmp

因为LINQ *可以最好地解决所有问题:
*并非所有内容都能通过LINQ解决。

 using System.Collections.Generic; using System.IO; using System.Linq; class Program { static void Main(string[] args) { List Files = new List() { @"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以确保文件名中有一些相同的字母,但不包括在内。

我不认为使用powershell方法来进行这种比较有一个“技巧”,但你可以在某种程度上优化你的解决方案:

 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 

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

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

 List list1 = new List(); 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 

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

 using System; using System.Collections.Generic; using System.IO; namespace stackoverflow1 { class MainClass { public static void Main (string[] args) { List paths=new List(); 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 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 

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

保持每个段的计数并继续前进,而所有路径都以计数开始。

 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(); var common = paths[0].Split('\\').TakeWhile ( segment => { test.Add ( segment ); return paths.All ( path => path.StartsWith ( String.Join ("\\", test ) + "\\") ) ; } ); Console.WriteLine ( String.Join ("\\", common ) ); } 

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

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

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

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

这样更好: 查找字符串的公共前缀

然而

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

应该

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