This feels like a silly question, but I have a string like:
aaaa/bbb\/ccc
The \/ represents an escaped delimiter being used in the name of a path component.
So, the string represents two path components aaaa and bbb/ccc
This string is generated based on a need to create a path from path components where the need is to use / as the delimiter between components and / may also appear in a component name. This is the reason behind the need to escape / when it appears in a component name.
There may be two or more components.
Using a regex like (?:\\\/|[^\/])+ is close to what I am looking for, but when considering the string this/is\/a/\/str\\/ing, it fails to split it into the components this & is\/a & \/str\\ & ing.
Instead, the final component is determined to be \/str\\/ing.
My question is what does the javascript code look like that would allow me to split paths into path components when the component delimiter can be used in the name of a component?
In the example above, I would want to end up with two strings aaaa and bbb/ccc?
Is there a standard function that deals with this or would I need to use a regex to help me split?
Thank you.
Using a match, you might use:
(?:[^\n\/\\]+|\\[\\\/]?)+
Explanation
(?: No capture group
[^\n\/\\]+ Match any char except a newline / or \| Or\\[\\\/]? Match \ and optional \ or /)+ Close non capture group and repeat 1+ timesThen in the matches, you can replace \/ with /
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("\\/", "/")))
);
If a lookbehind is supported you might use split with an alternation to match 2 scenario's where the string should split.
Then you can replace \/ with / for the result array using Array map for example.
(?<=\\\\)\/|(?<!\\)\/
Explanation
(?<=\\\\)\/ Match / when directly preceded by \\| Or(?<!\\)\/ Match / when not preceded by \[
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("\\/", "/"))));
First, because this involves javascript and its escaping rules, the string aaaa/bbb\/ccc needs to be aaaa/bbb\\/ccc.
This is my current solution:
//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}`);
});
I need to run the matches through a second pass so I can convert the match:
bbb\\/ccc
Into something that will be displayed properly. Without the second pass, it would display as
bbb\/ccc
and needs to display as:
bbb/ccc
Case #1
path = "aaaa/\\bbb";
I see displayed:
C: aaaa
C: \bbb
Case #2
path = "this/is\\/a/dumb/str\\\\/ing";
I see displayed:
C: this
C: is/a
C: dumb
C: str\\
C: ing
Case #3 (similar to #2)
path = "aaaa/bbb\\/ccc";
I see displayed:
C: aaaa
C: bbb/ccc
SUCCESS in all cases.
I believe I have caught all of the edge cases here.
Turns out this is a harder problem than I originally thought.
const a = String.raw `aaaa/bbb\/ccc/ddd\/eee/fff/ggg`;
console.log(a.replace(/\\\//g, "|").split("/").map(x => x.replaceAll("|", "/")));