从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
答案 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));
})