【问题标题】:How to check if one path is a child of another path?如何检查一条路径是否是另一条路径的子路径?
【发布时间】:2011-12-26 20:52:16
【问题描述】:

如何检查一条路径是否是另一条路径的子路径?
仅检查子字符串不是一种方法,因为可以有诸如 .和..等

【问题讨论】:

    标签: c# .net windows path


    【解决方案1】:

    不幸的是它不像StartsWith那么简单。

    这是一个更好的答案,改编自this重复问题。为了便于使用,我将其作为扩展方法。此外,使用蛮力catch 几乎任何访问文件系统的方法都可能因用户权限而失败。

    public static bool IsSubDirectoryOf(this string candidate, string other)
    {
        var isChild = false;
        try
        {
            var candidateInfo = new DirectoryInfo(candidate);
            var otherInfo = new DirectoryInfo(other);
    
            while (candidateInfo.Parent != null)
            {
                if (candidateInfo.Parent.FullName == otherInfo.FullName)
                {
                    isChild = true;
                    break;
                }
                else candidateInfo = candidateInfo.Parent;
            }
        }
        catch (Exception error)
        {
            var message = String.Format("Unable to check directories {0} and {1}: {2}", candidate, other, error);
            Trace.WriteLine(message);
        }
    
        return isChild;
    }
    

    【讨论】:

    • 吞下异常(不知道如何使用此方法)似乎是一种不好的做法。例如假设该方法用于防止从包含敏感数据的某个文件夹上传信息,现在当发生异常时将上传数据。
    • 这也不适用于区分大小写的路径。 IsSubDirectoryOf(@"c:\a\b", @"c:\A") 返回错误
    • 这个答案没有说明为什么看似显而易见的解决方案 .StartsWith 不起作用,而是它只是说明并解释了更复杂的解决方案 (-1)
    • 请注意,如果您尝试比较不同大小写的路径和其他一些边缘情况,这将不起作用,不幸的是 DirectoryInfo does not handle all of the quirks of Windows's file systems.
    • 两个目录都必须存在此方法才能工作,这是一个严重的限制。
    【解决方案2】:

    任何基于字符串的解决方案都可能受到directory traversal attacks 或尾部斜杠等正确性问题的影响。不幸的是,.NET Path 类不提供此功能,但 Uri 类以Uri.IsBaseOf() 的形式提供。

        Uri potentialBase = new Uri(@"c:\dir1\");
    
        Uri regular = new Uri(@"c:\dir1\dir2");
    
        Uri confusing = new Uri(@"c:\temp\..\dir1\dir2");
    
        Uri malicious = new Uri(@"c:\dir1\..\windows\system32\");
    
        Console.WriteLine(potentialBase.IsBaseOf(regular));   // True
        Console.WriteLine(potentialBase.IsBaseOf(confusing)); // True
        Console.WriteLine(potentialBase.IsBaseOf(malicious)); // False
    

    【讨论】:

    • IsBaseOf 似乎不适用于此。给定输入 'C:\somerandomdir''C:\someotherdir',我得到了一个真实的结果。
    • @jpmc26,那是因为你没有尾部斜杠。无法知道“somerandomdir”是目录名而不是文件名。如果您想处理这种情况,请在调用之前添加一个斜杠。
    • 为什么重要?无论如何,一个文件不能成为另一个文件的基础。为什么IsBaseOf 甚至做出如此奇怪的猜测,比如砍掉它认为是文件名的东西,而这显然不是来电者提出的问题?如果有这样的警告和奇怪的细节需要担心,你的答案至少应该解决它们。
    • 这是最安全最好的解决方案
    【解决方案3】:

    我用过这样的扩展方法:

        /// <summary>
        /// Check if a directory is the base of another
        /// </summary>
        /// <param name="root">Candidate root</param>
        /// <param name="child">Child folder</param>
        public static bool IsBaseOf(this DirectoryInfo root, DirectoryInfo child)
        {
            var directoryPath = EndsWithSeparator(new Uri(child.FullName).AbsolutePath);
            var rootPath = EndsWithSeparator(new Uri(root.FullName).AbsolutePath);
            return directoryPath.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase);
        }
    
        private static string EndsWithSeparator(string absolutePath)
        {
            return absolutePath?.TrimEnd('/','\\') + "/";
        }
    

    【讨论】:

      【解决方案4】:

      这是我的解决方案:

      // length parentDirectory <= length subDirectory
      static private bool isParentDirectory(string parentDirectory, string subDirectory)
      {
          if (!subDirectory.Contains(parentDirectory)) return false;
      
          string[] folders1 = subDirectory.Split('\\');
          string[] folders2 = parentDirectory.Split('\\');
      
          for (int i = 0; i < folders2.Count(); i++)
          {
              if (folders2[i] != folders1[i])
                  return false;
          }
          return true;
      }
      

      【讨论】:

      • 欢迎来到 StackOverflow。请提供一些解释,而不仅仅是代码。
      • 这是有问题的,因为它假设文件分隔符是`, when it could be .Net Core running on linux where the file separator is /. It also seems pointless to split, then compare each segment. Isn't that the same as a StartsWith`?
      【解决方案5】:

      在 C# 中你可以这样做:

      string cp = Path.GetFullPath(childPath);
      string pp = Path.GetFullPath(parentPath);
      
      if(pp.StartsWith(cp))
          return true;
      else
          return false;
      

      【讨论】:

      • 我建议使用“StartsWith”的重载来忽略大小写。
      • @Chad GetFullPath 将以相同的大小写返回,因此在这里忽略大小写没有用处
      【解决方案6】:

      我发现这适用于 windows:

      if (pathA.Equals(pathB, StringComparison.OrdinalIgnoreCase) ||
          pathA.StartsWith(pathB + "\\", StringComparison.OrdinalIgnoreCase))
      

      如果你的路径可能有尾随字符,你可以先这样规范化它们:

      pathA = Path.GetFullPath(pathA);
      pathB = Path.GetFullPath(pathB);
      

      【讨论】:

        【解决方案7】:

        有同样的问题。如果路径为字符串,则可以使用StartWith()

        if (pathA.StartsWith (pathB + "\\")) {
        

        虽然我不确定它是否是跨平台的,但它确实可以在 PC 上运行

        【讨论】:

          【解决方案8】:

          这是一种方法,你有路径 A 和 B,使用 Path.GetFullPath() 函数将它们转换为完整路径。接下来检查一个完整路径是否是另一个的起始子字符串。

          原来如此

          if (Path.GetFullPath(A).StartsWith(Path.GetFullPath(B)) ||
              Path.GetFullPath(B).StartsWith(Path.GetFullPath(A)))
             { /* ... do your magic ... */ }
          

          【讨论】:

          • 但是,如果在 Windows 上并且一个路径是小写而另一个是大写,这将不起作用。
          • 如果AC:\my\dir 并且BC:\my\dir2,这是否正常工作?那应该是false,但我认为Path.GetFullPath(B).StartsWith(Path.GetFullPath(A)) 应该是true
          • 您可以在实际目录名称上附加斜杠。如果不区分大小写,那么开始会起作用
          • @jrh 我相信@pinkfloydx33 的建议解决了这个答案中的问题。如果将答案编辑为包含它,那就太好了,但.StartsWith 似乎是此处发布的其他答案的一个体面、更简单的替代方案。更重要的是,其他答案没有提供任何充分的理由说明 .StartsWith 不起作用。 @Charlie 投票第二高的答案没有说明为什么 .StartsWith 不起作用,它只是声明了它。
          • @jrh 如您所见,GetFullPath 处理“..”。只需执行$"{input.TrimEnd(Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar)}" 即可解决斜杠问题。我理解您为什么认为 DirectoryInfo 是这样做的“正确”方式,但它要慢得多并且通常是不必要的。此外,.StartsWith 似乎是直观的解决方案,我认为这个答案不值得负分。
          猜你喜欢
          • 2014-05-05
          • 2016-08-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多