【问题标题】:Splitting a path when the path delimiter is used in a component name在组件名称中使用路径分隔符时拆分路径
【发布时间】:2021-10-11 16:39:22
【问题描述】:

这感觉像是一个愚蠢的问题,但我有一个字符串:

aaaa/bbb\/ccc

\/ 表示在路径组件名称中使用的转义分隔符。

所以,字符串代表两个路径分量aaaabbb/ccc

此字符串是基于从路径组件创建路径的需要而生成的,其中需要使用 / 作为组件之间的分隔符,并且 / 也可能出现在组件名称中。这就是在组件名称中出现 / 时需要转义的原因。

可能有两个或多个组件。

使用像(?:\\\/|[^\/])+ 这样的正则表达式接近我正在寻找的内容,但是在考虑字符串this/is\/a/\/str\\/ing 时,它无法将其拆分为组件thisis\/a\/str\\ 和@987654332 @。

相反,最终组件被确定为\/str\\/ing

我的问题是,当组件分隔符可以用于组件名称时,javascript 代码是什么样的,它允许我将路径拆分为路径组件?

在上面的示例中,我希望得到两个字符串 aaaabbb/ccc

是否有处理此问题的标准函数,或者我是否需要使用正则表达式来帮助我拆分?

谢谢。

【问题讨论】:

  • 只有两个组件吗?
  • 可以有一个或多个组件。
  • 这个字符串是基于从路径组件创建路径的需要而生成的,其中需要使用/作为组件之间的分隔符并且/也可能出现在组件名称中。我希望这已经足够好了。这就是在组件名称中出现 / 时需要转义的原因。它正在一个电子应用程序中使用,该应用程序也可能在浏览器中运行。
  • @pilchard 这些路径与文件名或 posix 路径无关。上下文不同。我同意,我希望做出另一个选择。

标签: javascript split path


【解决方案1】:

const a = String.raw `aaaa/bbb\/ccc/ddd\/eee/fff/ggg`;
console.log(a.replace(/\\\//g, "|").split("/").map(x => x.replaceAll("|", "/")));

【讨论】:

  • 当它关闭时,它对于aaaa/bbb\\/ccc/ddd\/eee/fff/ggg 失败,其中应该有组件bbb\ccc。它生成单个组件bbb\\/ccc
  • 我很想知道您对我的回答有何看法...
  • 我认为这是一个糟糕的设计选择,如上所述。将单独的路径值存储在字符串数组中或将 / 分隔符替换为 |或逗号以避免任何歧义。例如,aaaa|bbb/ccc|ddd。那么 / 周围就没有歧义了,也没有必要逃避。我不确定\/ 是否有任何影响,这是您问题的一部分。例如 const a = 'aaa\/bbb' 的解释方式与 const a = 'aaa/bbb' 相同。你真的能逃脱正斜杠吗?
  • 如果绝对必须使用 / 作为分隔符,为了避免分隔符不明确,请使用文本限定符,即 "aaaa"/"bbb/ccc",这是一种 CSV 文件标准方法。
  • 文本限定符是个好主意。我会玩它,看看它是如何工作的。使用的存储格式是 JSON。我将不得不看看它是否像 CSV 一样支持这个想法。当然,我相信需要处理类似的情况,其中组件名称也包含" 字符。
【解决方案2】:

首先,因为这涉及到javascript及其转义规则,字符串aaaa/bbb\/ccc需要是aaaa/bbb\\/ccc

这是我目前的解决方案:

  //path = "aaaa/\\bbb";
  //path = "this/is\\/a/dumb/str\\\\/ing";
  path = "aaaa/bbb\\/ccc";
  
  console.log("Path: ", path);

  const matches = path.match(/((?:[^\/\\]|\\\/|\\\\|\\)+)/g);

  console.log("M: ", matches);

  const pathComponents = matches.reduce((accumulator, component) => {
    component = component.replace("\\/", "/");
    accumulator.push(component);
    return accumulator;
  }, []);

  console.log("Path Components: ", pathComponents);
      
  pathComponents.forEach((component) => {
    console.log(`C: ${component}`);
  });
                

我需要通过第二遍运行匹配,以便转换匹配:

bbb\\/ccc

变成可以正确显示的东西。如果没有第二遍,它将显示为

bbb\/ccc

并且需要显示为:

bbb/ccc

案例#1

path = "aaaa/\\bbb";

我看到显示:

C: aaaa
C: \bbb

 

案例#2

path = "this/is\\/a/dumb/str\\\\/ing";

我看到显示:

C: this
C: is/a
C: dumb
C: str\\
C: ing

案例#3(类似于#2)

path = "aaaa/bbb\\/ccc";

我看到显示:

C: aaaa
C: bbb/ccc
     

在所有情况下都成功。

我相信我已经抓住了这里所有的边缘情况。

事实证明这是一个比我最初想象的更难的问题。

【讨论】:

【解决方案3】:

使用匹配,您可以使用:

(?:[^\n\/\\]+|\\[\\\/]?)+

说明

  • (?:没有捕获组
    • [^\n\/\\]+ 匹配除换行符之外的任何字符 /\
    • |或者
    • \\[\\\/]? 匹配 \ 和可选的 \/
  • )+关闭非捕获组并重复1次以上

Regex demo

然后在比赛中,您可以将\/替换为/

const regex = /(?:[^\n\/\\]+|\\[\\\/]?)+/g;
[
  String.raw `aaaa/bbb\/ccc`,
  String.raw `this/is\/a/\/str\\/ing`,
  String.raw `aaaa/bbb\\/ccc/ddd\/eee/fff/ggg`,
  String.raw `this/is\/a/dumb/str\\/ing`,
  String.raw `aaaa/\\bbb`
].forEach(s =>
  console.log(Array.from(s.matchAll(regex), m => m[0].replace("\\/", "/")))
);

如果向后看is supported,您可能会使用 split 和交替来匹配字符串应该拆分的 2 个场景。

然后您可以将 \/ 替换为 / 用于结果数组,例如使用 Array map

(?<=\\\\)\/|(?<!\\)\/

说明

  • (?&lt;=\\\\)\/ 匹配 / 时直接位于 \\ 前面
  • |或者
  • (?&lt;!\\)\/ 匹配 /,如果前面没有 \

Regex demo

[
  String.raw `aaaa/bbb\/ccc`,
  String.raw `this/is\/a/\/str\\/ing`,
  String.raw `aaaa/bbb\\/ccc/ddd\/eee/fff/ggg`,
  String.raw `this/is\/a/dumb/str\\/ing`,
  String.raw `aaaa/\\bbb`
].forEach(s => console.log(
  s
  .split(/(?<=\\\\)\/|(?<!\\)\//)
  .map(m => m.replace("\\/", "/"))));

【讨论】:

  • 如果我正确理解了您的答案,则表明我的正则表达式将起作用(或可能起作用),但是,如果支持后视,则可以使用更简洁的正则表达式。我相信在您的情况下,仍然需要对组件进行第二次传递才能将 \/ 转换为 /
  • 嗯...由于某种原因,当我运行您的代码 sn-p 时,它给了我一个脚本错误。如果我将matches = path.match(/(?&lt;=\\\\)\/|(?&lt;!\\)\//g); 放在我的解决方案代码中,则没有任何匹配项。也许我不支持向后看。
  • @JamesHudson 然后在您的环境中不支持后视。我认为这种缩短的模式也可以。 (?:[^\n\/\\]+|\\[\\\/]?)+regex101.com/r/B6zmY2/1
  • 好的。是的,不支持后视。我确实用我的三个测试用例验证了您的缩短模式会产生相同的结果。谢谢。
  • @JamesHudson 我已经添加了另一个具有该缩短模式的示例。
猜你喜欢
  • 2013-02-23
  • 2014-07-29
  • 2015-02-09
  • 2012-05-17
  • 1970-01-01
  • 2014-01-16
  • 2018-04-25
  • 2011-06-03
  • 1970-01-01
相关资源
最近更新 更多