一个用于分割文件路径的正则表达式。

3
我想创建一个正则表达式来按照以下方案拆分路径:
Path:        c:\foo\bar\baz.txt
Root name:   c:
Parent path: c:\foo\bar
Filename:    baz.txt
Stem:        baz
Extension:   txt

这是我的代码。问题在于,当我使用没有扩展名的文件名时,它无法工作:

^(([aA-zZ]:)\\(?:[^:]+))\\(([^\\]+)\.([^\.]+))$

我可以将其更改为。
^(([aA-zZ]:)\\(?:[^:]+))\\(([^\\]+)(\.([^\.]+))?)$

但它不能将文件名拆分为文件主干和扩展名。

你不能用简单的正则表达式来完成这个操作。当然,你可以使用斜杠“/”分隔符来拆分字符串,但要将其组合成逻辑单位,则无法仅凭简单的正则表达式实现。 - Nikos M.
([a-z]:)?((\\[^\\]+)*?)\\([a-z.]+) 可以分割为逻辑单位,但不建议使用。 - Nikos M.
正则表达式在这里好像不是个好主意。为什么不使用你所用的语言中的string.indexof / substring呢? - Julian
输出的样子是什么? - Mechanic
@Leonardo 你的意思是什么?干杯。 - john c. j.
你最终想要什么?我的意思是输入->输出,你想要它们在捕获组中吗? - Mechanic
2个回答

3

将路径分割的一个可能的解决方案是使用正则表达式组 (演示在此):

使用 'c:\foo\bar\baz.txt' 来解释以下正则表达式:

  • 首先是驱动器: (?<drive>[a-z]:) -> 'c:'
  • 最后一个是文件: (?<file>(?<fname>\\w+)(?:\.(?<fext>\w+))?)
    • (?<fname>\\w+) -> 'baz'
    • (?<fext>\\w+) -> 'txt'
  • 然后父路径是 drive 加上重复的 \\\w+\\?: (?<ppath>(?<drive>[a-z]:)(?:\\\w+\\?)+)

因此完整路径为 drive + parent path + file:
(?<fullpath>(?<ppath>(?<drive>[a-z]:)(?:\\\w+\\?)+)(?<file>(?<fname>\w+)(?:\.(?<fext>\w+))?)$)

// Example without group
const path = 'c:\\foo\\bar\\baz.txt';
const regex = /([a-z]:)((?:\\?\w+\\)+)((\w+)(?:\.(\w+))?)/;
const found = path.match(regex);

// console.log(found); // Array ["c:\foo\bar\baz.txt", "c:", "\foo\bar\", "baz.txt", "baz", "txt"]
console.log('Root name:\t' + found[1]); // drive
console.log('Parent path:\t' + found[1]+found[2]); // Parent path
console.log('Filename:\t' + found[3]); // File name
console.log('Stem:\t\t' + found[4]); // Stem
console.log('Extension:\t' + found[5]); // Extension

// Java example with group
String s = "c:\\foo\\bar\\baz.txt";
Pattern p = Pattern.compile("(?<fullpath>(?<ppath>(?<drive>[a-z]:)(?:\\\\\\w+\\\\?)+)(?<file>(?<fname>\\w+)(?:\\.(?<fext>\\w+))?)$)");
Matcher m = p.matcher(s);

while (m.find()) {
    System.out.println("Path:\t\t" + m.group("fullpath"));
    System.out.println("Root name:\t" + m.group("drive"));
    System.out.println("Parent path:\t" + m.group("ppath"));
    System.out.println("Filename:\t" + m.group("file"));
    System.out.println("Stem:\t\t" + m.group("fname"));
    System.out.println("Extension:\t" + m.group("fext"));
}
/* OUTPUT */
Path:           c:\foo\bar\baz.txt
Root name:      c:
Parent path:    c:\foo\bar\
Filename:       baz.txt
Stem:           baz
Extension:      txt

它似乎使用了一些C++的特性,对吧?我只需要一个可以独立使用的正则表达式。 - john c. j.
@johnc.j. 抱歉,这是Java代码,我为我的测试编写了它,但我也会给你regex101上的链接。也许你想要关于这个正则表达式的解释? - thibsc
还有一种子字符串方法可行。但如果只是一个没有语言的正则表达式,我可以编辑我的答案。 - thibsc
这将会很有用 :) - john c. j.
@johnc.j. 已修复了没有扩展名的文件名。 - thibsc
显示剩余2条评论

3

您可以使用带有惰性量词和可选组的正则表达式:

^(([a-zA-Z]:)(?:\\[^:]+)?)\\(([^\\\n]+?)(?:\.([^.\n]+))?)$

正则表达式演示

在下一个非捕获分组即 (?:\.([^.\n]+))? 是可选匹配时,将([^\\]+?)设为懒惰匹配很重要,以避免它匹配过多。


1
经过一些思考,我倾向于同意你的看法 :) - john c. j.
1
已测试通过 c:\foo\bar\aaa.txtc:\foo\bar\aaac:\aaa.txtc:\aaa。以下正则表达式适用于我:^(([a-zA-Z]:)(?:\\[^:]+)?)\\(([^\\]+?)(?:\.([^.\n]+))?)$ - john c. j.
太好了,让我测试一下,然后再修改我的答案。 - anubhava
1
另一个改进:^(([a-zA-Z]:)(?:\\[^:]+)?)\\(([^\\\n]+?)(?:\.([^.\n]+))?)$。它将解决此问题:https://regexr.com/532td - john c. j.
1
我得离开一会儿处理一些事情。同时请检查这是否有效:https://regex101.com/r/QJ95IA/3 我稍后会跟进的。 - anubhava
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接