【问题标题】:Determine if a path is subdirectory of another in Node.js确定路径是否是 Node.js 中另一个路径的子目录
【发布时间】:2016-05-30 09:15:38
【问题描述】:

我正在处理MQTT handler,我想为每个有事件侦听器的父目录发出一个事件。例如:

如果有以下可用的 MQTT 路径,其中有下标——这些路径有事件监听器——

  • test
  • replyer/request
  • test/replyer/request

有人发布主题test/replyer/request/@issuer,应该有2个事件:testtest/replyer/request

鉴于任何路径都是可能的并且没有可用的有效事件列表,我们必须仅检查路径是否是另一个路径的父级。我们可以用正则表达式做到这一点吗?如果是这样,它会是什么样子?有没有更简单/更有效的解决方案?

【问题讨论】:

    标签: javascript regex node.js


    【解决方案1】:

    让 Node 自己完成工作。

    const path = require('path');
    const relative = path.relative(parent, dir);
    return relative && !relative.startsWith('..') && !path.isAbsolute(relative);
    

    它也为你做标准化。

    const path = require('path');
    
    const tests = [
      ['/foo', '/foo'],
      ['/foo', '/bar'],
      ['/foo', '/foobar'],
      ['/foo', '/foo/bar'],
      ['/foo', '/foo/../bar'],
      ['/foo', '/foo/./bar'],
      ['/bar/../foo', '/foo/bar'],
      ['/foo', './bar'],
      ['C:\\Foo', 'C:\\Foo\\Bar'],
      ['C:\\Foo', 'C:\\Bar'],
      ['C:\\Foo', 'D:\\Foo\\Bar'],
    ];
    
    tests.forEach(([parent, dir]) => {
        const relative = path.relative(parent, dir);
        const isSubdir = relative && !relative.startsWith('..') && !path.isAbsolute(relative);
        console.log(`[${parent}, ${dir}] => ${isSubdir} (${relative})`);
    });
    

    也可以跨驱动器在 Windows 上运行。

    [/foo, /foo] => false ()
    [/foo, /bar] => false (..\bar)
    [/foo, /foobar] => false (..\foobar)
    [/foo, /foo/bar] => true (bar)
    [/foo, /foo/../bar] => false (..\bar)
    [/foo, /foo/./bar] => true (bar)
    [/bar/../foo, /foo/bar] => true (bar)
    [/foo, ./bar] => false (..\Users\kozhevnikov\Desktop\bar)
    [C:\Foo, C:\Foo\Bar] => true (Bar)
    [C:\Foo, C:\Bar] => false (..\Bar)
    [C:\Foo, D:\Foo\Bar] => false (D:\Foo\Bar)
    

    【讨论】:

    • 中断["/foo", "/foo/..bar"]。 IE。对于名字以.. 开头的孩子。我猜你需要const TRAVERSE_PATTERN = path.sep === '\\' ? /^\.\.([\/\\]|$)/ : /^\.\.(\/|$)/;,然后用!TRAVERSE_PATTERN.test(relative)代替!relative.startsWith('..')。两个不同的正则表达式,因为 Windows 允许 \ 和 / 作为路径分隔符。
    • @panzi 但/foo 不是/foo/..bar 的父目录。因为第二条路径解析为/bar
    • 什么? /foo/foo/..bar的父目录。 /foo/..bar 解析为 /foo/..bar..bar 是一个有效的文件名。在我的示例中,..bar 之间没有 /
    • relative.startsWith('..') 是不负责任的。我可以直接命名文件......myfile
    【解决方案2】:

    2021 答案

    使用@Ilya 的解决方案。

    2017 年答案

    在 ES6 中。

    const isChildOf = (child, parent) => {
      if (child === parent) return false
      const parentTokens = parent.split('/').filter(i => i.length)
      return parentTokens.every((t, i) => child.split('/')[i] === t)
    }
    

    如果您正在使用 node.js 并且希望使其跨平台,请包含 path 模块并将 split('/') 替换为 split(path.sep)


    工作原理:

    所以,你想知道一个目录(比如home/etc/subdirectory)是否是另一个目录(比如home/etc)的子目录。

    它采用假设的childparent 路径,并使用split 将它们转换为数组:

    ['home', 'etc', 'subdirectory'], ['home', 'etc']
    

    然后它遍历 parent 数组中的所有标记,并使用 ES6 的 .every() 逐一检查它们在 child 数组中的相对位置。

    如果 parent 中的所有内容都与 child 中的所有内容匹配,并且知道我们已经排除了它们是完全相同的目录(使用 child !== parent),我们将得到答案。

    【讨论】:

      【解决方案3】:

      对于那些关心性能的人来说,似乎没有注意到已经回答的人,检查子路径是否以其父路径开始就足够了。

      const path = require('path');
      
      function isSubPathOf(subPath, parentPath) {
      
          parentPath = normalize(parentPath);
      
          if (subPath.length <= parentPath.length)
              return false;
      
          function normalize(p) {
      
              p = path.normalize(p);
      
              if (!p.endsWith(path.sep))
                  p += path.sep;
      
              return p;
          }
      
          subPath = normalize(subPath);
      
          return subPath.startsWith(parentPath);
      }
      
      console.log(isSubPathOf('/a/b/c/d/e', '/a/b/c'));
      console.log(isSubPathOf('/a/b/c/de', '/a/b/c'));
      console.log(isSubPathOf('/a/b/c', '/a/y/c'));
      console.log(isSubPathOf('/a/y/c/k', '/a/y/c'));
      

      【讨论】:

      • 这个答案可能需要一行来规范化/处理路径(例如,删除..
      • 感谢您的建议。我在答案中编辑了代码以根据需要规范化路径。
      • 我认为你可以使用path.resolve 来规范化路径
      • @Roymunson, path.resolve 会从输入路径中删除任何尾随斜杠,这是一件坏事,因为它会使 '/a/b/ca' 成为 '/a/b 的子路径/c'
      • 你的意思是if语句中的布尔表达式吗?在这种情况下,没有必要在该条件之前对两条路径进行规范化,因为如果预期的子路径比父路径短,则它不能是它的子路径。但是,我注意到父路径可能写得更长(例如,包括一些 ../),因此有必要在条件之前仅对父路径进行规范化。子路径不必进行规范化,因此出于性能原因,我将其保留在原处。我会立即更新我的答案。
      【解决方案4】:

      这是一个非常古老的问题,但我使用 Node 的内置 path.relative 提出了一个非常简单的解决方案:如果孩子在父母内部,则从前者到后者的相对路径将始终开始'..'

      import { relative } from 'path';
      
      function isSubDirectory(parent, child) {
          return relative(child, parent).startsWith('..');
      }
      

      【讨论】:

      • 这个和投票最高的答案基本一致:stackoverflow.com/a/45242825/2413043
      • 你说得对……我没有看到那个答案,因为答案太多了。哎呀
      • 这个答案不起作用。它允许所有以../ 开头的路径。因此,如果目录树上有任何可能的路径到父级(这很可能,尤其是在使用绝对路径的情况下),它会通过。试试isSubDirectory('A/B/C/D', 'A/B/C/Z/F')
      • path.relative('/a/b2', '/a/b').startsWith('..') => true
      【解决方案5】:

      这里有几件事需要防止失败:

      • 我们应该尝试解析文件系统路径吗? (我想是的)
      • 检查一个目录是否包含另一个目录应该适用于符号链接

      我想出了一个解决方案,尝试尽可能多地解析文件系统路径同时允许可能存在或可能不存在的路径

      • 在操作系统的路径分隔符上分割路径
      • 尽可能多地解析文件系统中的这些路径组件
      • 附加无法解析的剩余组件
      • 如果父与子之间的相对路径不是以.. + path.sep 开头且不是..,则父路径包含子路径

      这一切都有效,假设将仅使用目录和文件创建任何不存在的路径组件(无符号链接)。例如,假设您的脚本只需要写入列入白名单的路径,并且您正在接受不受信任的(用户提供的)文件名。您可以使用PHP's mkdir$recursive = true 之类的内容创建子目录,以一步创建目录结构,类似于this example

      这里是代码(直到 Stack Overflow 支持 Node.js 才能运行),重要的函数是 resolveFileSystemPath()pathContains()

      const kWin32 = false;
      
      const fs = require('fs');
      const path = kWin32 ? require('path').win32 : require('path');
      
      ////////// functions //////////
      
      // resolves (possibly nonexistent) path in filesystem, assuming that any missing components would be files or directories (not symlinks)
      function resolveFileSystemPath(thePath) {
      	let remainders = [];
      
      	for (
      		let parts = path.normalize(thePath).split(path.sep); // handle any combination of "/" or "\" path separators
      		parts.length > 0;
      		remainders.unshift(parts.pop())
      	) {
      		try {
      			thePath =
      				fs.realpathSync(parts.join('/')) + // fs expects "/" for cross-platform compatibility
      				(remainders.length ? path.sep + remainders.join(path.sep) : ''); // if all attempts fail, then path remains unchanged
      
      			break;
      		} catch (e) {}
      	}
      
      	return path.normalize(thePath);
      }
      
      // returns true if parentPath contains childPath, assuming that any missing components would be files or directories (not symlinks)
      function pathContains(parentPath, childPath, resolveFileSystemPaths = true) {
      	if (resolveFileSystemPaths) {
      		parentPath = resolveFileSystemPath(parentPath);
      		childPath = resolveFileSystemPath(childPath);
      	}
      
      	const relativePath = path.relative(parentPath, childPath);
      
      	return !relativePath.startsWith('..' + path.sep) && relativePath != '..';
      }
      
      ////////// file/directory/symlink creation //////////
      
      console.log('directory contents:');
      
      console.log();
      
      try {
      	fs.mkdirSync('parent');
      } catch (e) {} // suppress error if already exists
      
      fs.writeFileSync('parent/child.txt', 'Hello, world!');
      
      try {
      	fs.mkdirSync('outside');
      } catch (e) {} // suppress error if already exists
      
      try {
      	fs.symlinkSync(path.relative('parent', 'outside'), 'parent/child-symlink');
      } catch (e) {} // suppress error if already exists
      
      fs.readdirSync('.').forEach(file => {
      	const stat = fs.lstatSync(file);
      
      	console.log(
      		stat.isFile()
      			? 'file'
      			: stat.isDirectory() ? 'dir ' : stat.isSymbolicLink() ? 'link' : '    ',
      		file
      	);
      });
      fs.readdirSync('parent').forEach(file => {
      	file = 'parent/' + file;
      
      	const stat = fs.lstatSync(file);
      
      	console.log(
      		stat.isFile()
      			? 'file'
      			: stat.isDirectory() ? 'dir ' : stat.isSymbolicLink() ? 'link' : '    ',
      		file
      	);
      });
      
      ////////// tests //////////
      
      console.log();
      
      console.log(
      	"path.resolve('parent/child.txt'):       ",
      	path.resolve('parent/child.txt')
      );
      console.log(
      	"fs.realpathSync('parent/child.txt'):    ",
      	fs.realpathSync('parent/child.txt')
      );
      console.log(
      	"path.resolve('parent/child-symlink'):   ",
      	path.resolve('parent/child-symlink')
      );
      console.log(
      	"fs.realpathSync('parent/child-symlink'):",
      	fs.realpathSync('parent/child-symlink')
      );
      
      console.log();
      
      console.log(
      	'parent contains .:                                ',
      	pathContains('parent', '.', true)
      );
      console.log(
      	'parent contains ..:                               ',
      	pathContains('parent', '..', true)
      );
      console.log(
      	'parent contains parent:                           ',
      	pathContains('parent', 'parent', true)
      );
      console.log(
      	'parent contains parent/.:                         ',
      	pathContains('parent', 'parent/.', true)
      );
      console.log(
      	'parent contains parent/..:                        ',
      	pathContains('parent', 'parent/..', true)
      );
      console.log(
      	'parent contains parent/child.txt (unresolved):    ',
      	pathContains('parent', 'parent/child.txt', false)
      );
      console.log(
      	'parent contains parent/child.txt (resolved):      ',
      	pathContains('parent', 'parent/child.txt', true)
      );
      console.log(
      	'parent contains parent/child-symlink (unresolved):',
      	pathContains('parent', 'parent/child-symlink', false)
      );
      console.log(
      	'parent contains parent/child-symlink (resolved):  ',
      	pathContains('parent', 'parent/child-symlink', true)
      );

      输出:

      directory contents:
      
      file .bash_logout
      file .bashrc
      file .profile
      file config.json
      dir  node_modules
      dir  outside
      dir  parent
      link parent/child-symlink
      file parent/child.txt
      
      path.resolve('parent/child.txt'):        /home/runner/parent/child.txt
      fs.realpathSync('parent/child.txt'):     /home/runner/parent/child.txt
      path.resolve('parent/child-symlink'):    /home/runner/parent/child-symlink
      fs.realpathSync('parent/child-symlink'): /home/runner/outside
      
      parent contains .:                                 false
      parent contains ..:                                false
      parent contains parent:                            true
      parent contains parent/.:                          true
      parent contains parent/..:                         false
      parent contains parent/child.txt (unresolved):     true
      parent contains parent/child.txt (resolved):       true
      parent contains parent/child-symlink (unresolved): true
      parent contains parent/child-symlink (resolved):   false
      

      现场示例:https://repl.it/repls/LawngreenWorriedGreyware

      输出的最后一行是重要的,显示解析的文件系统路径如何导致正确的结果(与上面的未解析结果不同)。

      限制文件系统对某些目录的读/写对于安全性非常重要,我希望 Node.js 将此功能合并到它们的内置函数中。我还没有在本机 Windows 机器上对此进行测试,所以请让我知道 kWin32 标志是否有效。我会在时间允许的情况下尝试整理这个答案。

      【讨论】:

        【解决方案6】:

        基于和改进 Dom Vinyard 的代码:

        const path = require('path');
        
        function isAncestorDir(papa, child) {
            const papaDirs = papa.split(path.sep).filter(dir => dir!=='');
            const childDirs = child.split(path.sep).filter(dir => dir!=='');
        
            return papaDirs.every((dir, i) => childDirs[i] === dir);
        }
        

        结果:

        assert(isAncestorDir('/path/to/parent', '/path/to/parent/and/child')===true);
        assert(isAncestorDir('/path/to/parent', '/path/to')===false);
        assert(isAncestorDir('/path/to/parent', '/path/to/parent')===true);
        

        【讨论】:

          【解决方案7】:

          使用正则表达式是一种解决方法(对于每个具有事件侦听器的路径,请检查发布的主题是否以该路径开头),但由于您可能拥有许多不同的路径而不是您拥有的路径长得离谱的 URL,分解已发布的主题可能更有效。

          这样的东西可能也更容易阅读:

          编辑@huaoguo 绝对正确,indexOf === 0 是我们真正需要的!

          let paths = [
              'test',
              'replyer/request',
              'test/replyer/request'
          ]
          
          let topic = 'test/replyer/request/@issuer'
          
          let respondingPaths = (paths, topic) => paths.filter(path => topic.indexOf(path) === 0)
          
          console.log(respondingPaths(paths, topic)) // ['test', 'test/replyer/request']
          

          【讨论】:

          • 想过这个,但猜想有一个更优雅的解决方案。感谢您的回答。
          【解决方案8】:

          @dom-vinyard 的想法不错,但代码无法正常工作,例如使用此输入:

          isChildOf('/x/y', '/x') //false
          

          我在这里写了自己的版本:

          function isParentOf(child, parent) {
            const childTokens = child.split('/').filter(i => i.length);
            const parentTokens = parent.split('/').filter(i => i.length);
          
            if (parentTokens.length > childTokens.length || childTokens.length === parentTokens.length) {
              return false;
            }
          
            return childTokens
              .slice(0, parentTokens.length)
              .every((childToken, index) => parentTokens[index] === childToken);
          }
          

          【讨论】:

            【解决方案9】:

            这里是另一个使用 indexOf 的解决方案(或通过比较字符串来工作)。
            在下面的函数中,我没有使用indexOf支持多个路径分隔符。你可以检查一下,但如果你确定你只有一个分隔符,你可以使用indexOf 没有问题。
            诀窍检查路径是否以分隔符结尾如果不是,则添加这样的分隔符。这样,子路径中的子字符串不是完整路径就不会出现问题。 [/this/isme_man/this/isme] (如果我们简单地使用 indexOf (当然如果为假),第一个是第二个的孩子,但如果你确实使用这样的技巧 [/this/isme//this/isme_man/ ] 并且您使用相同的indexOf 进行比较不会有问题,并且它可以工作 nikel)]。
            还要注意,有一个选项允许使用 orEqual(子级或等于)进行检查,它是第三个可选参数。

            检查下面的代码。

            const PATH_SEPA = ['\\', '/'];
            
            function isPathChildOf(path, parentPath, orEqual) {
                path = path.trim();
                parentPath = parentPath.trim();
            
                // trick: making sure the paths end with a separator
                let lastChar_path = path[path.length - 1];
                let lastChar_parentPath = path[parentPath.length - 1];
                if (lastChar_parentPath !== '\\' && lastChar_parentPath !== '/') parentPath += '/';
                if (lastChar_path !== '\\' && lastChar_path !== '/') path += '/';
            
                if (!orEqual && parentPath.length >= path.length) return false; // parent path should be smaller in characters then the child path (and they should be all the same from the start , if they differ in one char then they are not related)
            
                for (let i = 0; i < parentPath.length; i++) {
                    // if both are not separators, then we compare (if one is separator, the other is not, the are different, then it return false, if they are both no separators, then it come down to comparaison, if they are same nothing happen, if they are different it return false)
                    if (!(isPathSeparator(parentPath[i]) && isPathSeparator(path[i])) && parentPath[i] !== path[i]) {
                        return false;
                    }
                }
                return true;
            }
            
            function isPathSeparator(chr) {
                for (let i = 0; i < PATH_SEPA.length; i++) {
                    if (chr === PATH_SEPA[i]) return true;
                }
                return false;
            }
            

            这里是一个测试示例:

            let path = '/ok/this/is/the/path';
            let parentPath = '/ok/this/is';
            let parentPath2 = '/ok/this/is/';
            let parentPath3 = '/notok/this/is/different';
            
            console.log("/ok/this/is/the/path' is child of /ok/this/is => " + isPathChildOf(path, parentPath));
            console.log("/ok/this/is/the/path' is child of /ok/this/is/=> " + isPathChildOf(path, parentPath2));
            console.log("/ok/this/is/' is child of /ok/this/is/ => " + isPathChildOf(parentPath2, parentPath2));
            console.log("/ok/this/is/the/path' is child of /notok/this/is/different => " + isPathChildOf(path, parentPath3));
            
            // test number 2:
            
            console.log('test number 2 : ');
            console.log("=============================");
            
            let pthParent = '/look/at/this/path';
            let pth = '/look/at/this/patholabi/hola'; // in normal use of indexof it will return true (know too we didn't use indexof just to support the different path separators, otherwise we would have used indexof in our function)
            
            //expected result is false
            console.log(`${pth}  is a child of ${pthParent}  ===>  ${isPathChildOf(pth, pthParent)}`);
            
            
            let pthParent2 = '/look/at/this/path';
            let pth2 = '/look/at/this/path/hola'; 
            
            //expected result is true
            console.log(`${pth2}  is a child of ${pthParent2}  ===>  ${isPathChildOf(pth2, pthParent2)}`);
            
            
            let pthParent3 = '/look/at/this/path';
            let pth3 = '/look/at/this/pathholabi'; 
            
            //expected result is false
            console.log(`${pth3}  is a child of ${pthParent3}  ===>  ${isPathChildOf(pth3, pthParent3)}`);
            
            // test 3: equality
            console.log('\ntest 3 : equality');
            console.log("==========================");
            
            let pParent =  "/this/is/same/Path";
            let p =  "/this\\is/same/Path/";
            
            console.log(`${p} is child of  ${pParent}   ====> ${isPathChildOf(p, pParent, true)}`);
            

            您可以在最后一个示例中看到我们如何使用该函数来检查是否为孩子或相等(这可能非常少)。

            还知道,您可以查看我的两个相关 github 存储库,其中还包括拆分方法的另一种实现(不使用正则表达式引擎的具有多个分隔符的拆分方法)),也可以使用此方法,以及一些很好的解释(检查代码中的 cmets):

            【讨论】:

              【解决方案10】:

              我还想指出 npm 包 path-is-inside,它完全符合 TO 的要求:

              用法(引自自述文件):

              很简单。首先是被测试的路径;然后是潜在的父母。像这样:

              var pathIsInside = require("path-is-inside");
              
              pathIsInside("/x/y/z", "/x/y") // true
              pathIsInside("/x/y", "/x/y/z") // false
              

              路径被认为在自身内部:

              pathIsInside("/x/y", "/x/y"); // true
              

              对我来说,它完成了这项工作,而且在一个额外的包中而不是 StackOverflow 答案中维护这种非平凡的逻辑肯定会更好。 :-)

              【讨论】:

                【解决方案11】:

                在我的理解中,这个问题比我的同事理解的要复杂一些。

                • 可以使用fs.existsSync() 测试路径,但在这种情况下,我们将创建对fs 库的依赖项,并将其限制为仅测试绝对目录,除非用户是有兴趣不使用Windows作为操作系统,这样就不需要通知根目录,默认情况下由“字母:\”形成,将分区视为彼此的姐妹,没有嵌套到更大的目录。 Unix 系统上的操作完全不同。根目录默认为/,所有挂载的磁盘都嵌套在其中,使其对整个操作系统都是唯一的。因此可以看出,这并不理想。

                • 考虑到如果“pathFoldersLength”大于“path2CheckIfIsSupposedParentFoldersLength”,则不可能仅使用正则表达式解决,例如,如果“path”等于“laden /subfolder- 1”和“path2CheckIfIsParent”到“bin/laden/subfolder-1/subfolder-1.1”,如果搜索是在“路径”末尾使用$完成的;例如,如果“path”等于“bin/laden/subfolder-1/subfolder-1.1”且“path2CheckIfIsParent”等于“laden/subfolder-1”,则不带$ 离开会产生误报;

                  如果“pathFoldersLength”小于“path2CheckIfIsSupposedParentFoldersLength”,例如,如果“path”等于“laden/subpath-1”且“path2CheckIfIsParent”等于“bin/laden/ subpath-1”,您可能会得到假阴性" 如果搜索是在 "path2CheckIfIsParent" 开头使用 ^ 完成的,或者如果例如 "path" 等于 "bin/laden" 并且 "path2CheckIfIsParent" 等于 " bin/laden",它可能会给出误报/subpath-1"。

                  通过这种方式,我们消除了依赖关系,使方法尽可能少地依赖语言。

                
                import Path from 'path';
                
                const isASubpathFromAnyOfSubpathSet = (path, path2CheckIfIsParent, isAbsolute = true) => {
                
                  const pathSeparatorPattern = new RegExp(/\\+/, 'g');
                
                  const pathSeparatorAtStartOrAtEndPattern = new RegExp(/^\/+|\/+$/, 'g');
                
                  path = path.replace(pathSeparatorPattern, `/`);
                
                  path2CheckIfIsParent = path2CheckIfIsParent.replace(pathSeparatorPattern, `/`);
                
                  path = path.replace(pathSeparatorAtStartOrAtEndPattern , ``);
                
                  path2CheckIfIsParent = path2CheckIfIsParent.replace(pathSeparatorAtStartOrAtEndPattern , ``)
                
                  if (path === path2CheckIfIsParent) return false;
                  
                  const pathFolders = path.split(`/`);
                
                  const path2CheckIfIsSupposedParentFolders = path2CheckIfIsParent.split(`/`);
                
                  const pathFoldersLength = pathFolders.length;
                
                  const path2CheckIfIsSupposedParentFoldersLength = path2CheckIfIsSupposedParentFolders.length;
                
                  const indexesOf = [];
                
                  let pathFolderIndex = 0;
                
                  let supposedParentFolderIndex = 0;
                
                  let stopCriterian;
                
                  for (let i = 0; i < path2CheckIfIsSupposedParentFoldersLength; i++) {
                    if (pathFolders[0] === path2CheckIfIsSupposedParentFolders[i]) indexesOf.push(i);
                  }
                
                  if (indexesOf.length) {
                
                    if (isAbsolute) {
                
                      if (pathFoldersLength > path2CheckIfIsSupposedParentFoldersLength) {  
                        path2CheckIfIsParent = path2CheckIfIsParent.replace(/\./g, `\\.`);
                
                        return (new RegExp(`^${path2CheckIfIsParent}`)).test(path);
                
                      }
                
                    } else {
                      
                      if (indexesOf[0] === 0) indexesOf.shift();
                
                      if (pathFoldersLength < path2CheckIfIsSupposedParentFoldersLength) {
                        stopCriterian = () => pathFolderIndex < pathFoldersLength - 1;
                      } else {
                        stopCriterian = () => supposedParentFolderIndex < path2CheckIfIsSupposedParentFoldersLength - 1;
                      }
                
                      for (let indexOf of indexesOf) {
                
                        pathFolderIndex = 0;
                        
                        for (supposedParentFolderIndex = indexOf; stopCriterian();) {
                          
                          if (path2CheckIfIsSupposedParentFolders[supposedParentFolderIndex] !== pathFolders[pathFolderIndex]) break;
                
                          supposedParentFolderIndex++;
                          pathFolderIndex++;
                
                        }
                
                      }
                
                      if (pathFoldersLength < path2CheckIfIsSupposedParentFoldersLength) {
                
                        return pathFolderIndex === pathFoldersLength - 1;
                
                      } else {
                        
                        return supposedParentFolderIndex === path2CheckIfIsSupposedParentFoldersLength - 1;
                
                      }
                
                    }
                
                  }
                  
                  return false;
                
                }
                
                
                /*
                // >
                console.log(isASubpathFromAnyOfSubpathSet (`bin/laden/subfolder-1`, `bin/laden`)) // => true
                console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1/subfolder-1.1.1`, `bin/laden/subfolder-1`)) // => false
                console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1`, `bin/laden`)) // => false
                
                console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1/subfolder-1.1.1`, `bin/laden/subfolder-1`, false)) // => true
                console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1`, `bin/laden`, false)) // => true
                
                
                // <
                console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1`, `bin/laden/subfolder-1/subfolder-1.1`, false)) // => true
                console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1`, `bin/laden/subfolder-1`, false)) // => true
                console.log(isASubpathFromAnyOfSubpathSet (`subfolder-1/subfolder-1.1`, `bin/laden/subfolder-1`, false)) // => true
                
                
                // ===
                console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1`, `bin/laden/subfolder-1`, false)) // => true
                console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1`, `bin/laden`, false)) // => true
                /**/
                

                【讨论】:

                  【解决方案12】:

                  使用indexOf检查子目录的路径是否以父目录的路径开头就够了:

                  function isParentOf(parent, dir) {
                      return dir.indexOf(parent) === 0;
                  }
                  
                  isParentOf('test/replyer/request/@issuer', 'test') // true
                  isParentOf('test/replyer/request/@issuer', 'replyer/request') // false
                  isParentOf('test/replyer/request/@issuer', 'test/replyer/request') // true
                  

                  【讨论】:

                  • 非常感谢,这很简单。
                  • isParentOf('test/notquite', 'test/no') // true
                  • isParentOf('test/quite/', 'test/quite/../notquite/') // true
                  • isParentOf('test/quite/', 'test\\quite\\sub') // 错误
                  猜你喜欢
                  • 2012-02-09
                  • 2016-08-18
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-12-26
                  • 2017-10-12
                  • 2014-05-05
                  • 1970-01-01
                  相关资源
                  最近更新 更多