3

我有以下方法:

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(..., ...)

有任何想法吗?

4

6 回答 6

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) );
于 2013-04-16T21:20:19.887 回答
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);
于 2013-04-16T21:20:37.850 回答
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;

    });
于 2013-04-16T23:54:06.260 回答
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,您可以将其源代码复制到您的项目中。

于 2013-04-16T22:56:15.733 回答
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 + "/"));
    }
}
于 2013-04-16T21:23:52.257 回答
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 ;
  }
}

应该做你。

于 2013-04-16T21:40:07.527 回答