我有以下方法:
private List<string> CreateSegments(string virtualPath)
{
List<string> segments = new List<string>();
int i = virtualPath.IndexOf('/', 1);
while (i >= 0 && i < virtualPath.Length)
{
var segment = virtualPath.Substring(0, i);
if (!string.IsNullOrWhiteSpace(segment))
{
segments.Add(segment);
segments.Add(VirtualPathUtility.Combine(segment, "default"));
}
i = virtualPath.IndexOf('/', i + 1);
}
segments.Add(virtualPath);
segments.Add(VirtualPathUtility.Combine(virtualPath, "default"));
return segments;
}
基本上,它会创建路径段,我将用它来检查任何这些段中是否存在文件。像这样:
string[] extensions = GetRegisteredExtensions();
HttpServerUtilityBase server = HttpContext.Current.Server;
List<string> segments = CreateSegments(virtualPath);
// check if a file exists with any of the registered extensions
var match = extensions.SelectMany(x => segments.Select(s => string.Format("{0}.{1}", s, x)))
.FirstOrDefault(p => System.IO.File.Exists(server.MapPath(p)));
以上所有代码看起来都可以使用一些清理和优化,但我正在寻找一种方法来使用LINQ
,如果可能的话,生成段。
类似于:var segments = virtualPath.Split('/').SelectMany(...)
并获得类似于以下内容的结果:
/path
/path/default
/path/to
/path/to/default
/path/to/file
/path/to/file/default
virtualPath
包含值"/path/to/file"
编辑:将string.Format("{0}/{1}", ...)
更改为VirtualPathUtility.Combine(..., ...)
有什么想法吗?
答案 0 :(得分:3)
一种方法是逐步选择路径段,然后用空字符串和"/default"
“连接”它以获得两个变体:
string path = @"/path/to/file";
string temp = "";
var query = path.Split('/')
.Where(s => !string.IsNullOrEmpty(s))
.Select((p) => {temp += ("/" + p); return temp;} )
.SelectMany(s => new[]{"","/default"}.Select (d => s + d) );
答案 1 :(得分:2)
如果您首先定义这样的扩展方法:
public static IEnumerable<int> SplitIndexes(this string subject, char search)
{
for(var i = 1; i < subject.Length; i++)
{
if(subject[i] == search)
{
yield return i;
}
}
yield return subject.Length;
}
然后你可以这样做:
var endings = new string[] { string.Empty, "/default" };
var virtualPath = "/path/to/file";
var results =
from i in virtualPath.SplitIndexes('/')
from e in endings
select virtualPath.Substring(0, i) + e;
或者如果您更喜欢查询语法:
var endings = new string[] { string.Empty, "/default" };
var virtualPath = "/path/to/file";
var results = virtualPath.SplitIndexes('/')
.SelectMany(i => endings.Select(e => virtualPath.Substring(0, i) + e));
结果将是:
/path
/path/default
/path/to
/path/to/default
/path/to/file
/path/to/file/default
正如其他人所建议的那样,您可以使用Path.Combine
以更加独立于平台的方式进行此操作,如下所示:
var endings = new string[] { string.Empty, "default" }; // Note: no / before default
var results =
from i in virtualPath.SplitIndexes(Path.DirectorySeparatorChar)
from e in endings
select Path.Combine(virtualPath.Substring(0, i), e);
答案 2 :(得分:2)
您正在寻找的高阶函数称为scan。普通LINQ中没有这样的功能,但您可以在MoreLinq中找到它。使用它,您的代码可能如下所示:
private List<string> CreateSegments(string virtualPath)
{
return virtualPath.Split('/')
.Scan((s1, s2) => s1 + '/' + s2)
.Skip(1)
.SelectMany(p => new[] { p, p + "/default" })
.ToList();
}
这假设您的路径始终是以/
开头的绝对路径。对于相对路径,您需要删除.Skip(1)
部分。
如果您不想仅为此方法获取MoreLinq,则只需将its source复制到项目中即可。
答案 3 :(得分:2)
这可能会成功。它不是最简洁的代码,但它对我来说似乎非常易读。它使用字符串连接,因为对于短字符串,如路径或URL,它比任何替代方案都快。
编辑:修复并经过测试。
var query = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries)
.Aggregate(new List<string>(), (memo, segment) => {
memo.Add(memo.DefaultIfEmpty("").Last() + "/" + segment);
return memo;
}).Aggregate(new List<string>(), (memo, p) => {
memo.Add(p);
memo.Add(p + "/default");
return memo;
});
答案 4 :(得分:0)
到目前为止提供的答案更简洁,但这就是我想出的:
public static IEnumerable<string> PossiblePaths(string basePath)
{
return PossiblePaths(basePath.Split(new[] { "/" },
StringSplitOptions.RemoveEmptyEntries));
}
private static IEnumerable<string> PossiblePaths(IEnumerable<string> segments,
string current = "/")
{
if (segments.Count() == 0)
{
return new string[0];
}
else
{
string next = current + segments.First();
return new[] { next, next + "/default" }
.Concat(PossiblePaths(segments.Skip(1), next + "/"));
}
}
答案 5 :(得分:0)
这样的事情:
public static IEnumerable<string> EnumerateSegments( this IEnumerable<string> segments )
{
StringBuilder sb = new StringBuilder() ;
foreach ( string segment in segements )
{
sb.Append( Path.DirectorySeparatorChar ).Append( segment ) ;
yield return sb.ToString() ;
int n = sb.Length ;
sb.Append( Path.DirectorySeparatorChar ).Append("default") ;
yield return sb.ToString() ;
sb.Length = n ;
}
}
应该做你。