如何从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)); })