【问题标题】:Extract filename parts to solve file name conflict提取文件名部分以解决文件名冲突
【发布时间】:2019-12-10 21:21:44
【问题描述】:

在 c# 程序中,我想在可能存在其他文件的文件夹中写入文件。如果是这样,可以在文件中添加后缀myfile.docxmyfile (1).docxmyfile (2).docx 等等。

我正在努力分析现有文件名以提取现有文件的名称​​部分

特别是,我使用这个正则表达式:(?<base>.+?)(\((?<idx>\d+)\)?)?(?<ext>(\.[\w\.]+))

这个正则表达式输出:

╔═══════════════════════╦══════════════╦═════╦═══════════╦═══════════════════════════════════╗
║    Source Filename    ║     base     ║ idx ║ extension ║              Success              ║
╠═══════════════════════╬══════════════╬═════╬═══════════╬═══════════════════════════════════╣
║ somefile.docx         ║ somefile     ║     ║ .docx     ║ Yes                               ║
║ somefile              ║              ║     ║           ║ No, base should be "somefile"     ║
║ somefile (6)          ║              ║     ║           ║ No, base should be "somefile (6)" ║
║ somefile (1).docx     ║ somefile     ║   1 ║ .docx     ║ Yes                               ║
║ somefile (2)(1).docx  ║ somefile (2) ║   1 ║ .docx     ║ Yes                               ║
║ somefile (4).htm.tmpl ║ somefile     ║   4 ║ .htm.tmpl ║ Yes                               ║
╚═══════════════════════╩══════════════╩═════╩═══════════╩═══════════════════════════════════╝

如您所见,所有情况都有效,但文件名没有扩展名时除外。

如何修复我的正则表达式来解决失败的情况?

转载:https://regex101.com/r/q9uQii/1

如果有关系,这里是相关的 C# 代码:

private static readonly Regex g_fileNameAnalyser = new Regex(
    @"(?<base>.+?)(\((?<idx>\d+)\)?)?(?<ext>(\.[\w\.]+))", 
    RegexOptions.Compiled | RegexOptions.ExplicitCapture
    );

...

var candidateMatch = g_fileNameAnalyser.Match(somefilename);
var candidateInfo = new
{
    baseName = candidateMatch.Groups["base"].Value.Trim(),
    idx = candidateMatch.Groups["idx"].Success ? int.Parse(candidateMatch.Groups["idx"].Value) : 0,
    ext = candidateMatch.Groups["ext"].Value
};

【问题讨论】:

  • 为什么不在循环中检查文件是否存在,将索引递增一,直到找到可用的文件名?
  • 使用^(?&lt;base&gt;.+?)(?:\((?&lt;idx&gt;\d+)\))?(?&lt;ext&gt;\.[\w.]+)?$^(?&lt;base&gt;.+?)\s*(?:\((?&lt;idx&gt;\d+)\))?(?&lt;ext&gt;\.[\w.]+)?$,见demo
  • @PeterWolf:这可能是一个解决方案。我会考虑的
  • @WiktorStribiżew:您的正则表达式似乎按预期工作。你应该发布一个答案
  • 对于最后一个示例,请注意操作系统将.tmpl 视为扩展名,将somefile (4).htm 视为文件名,请参阅此fiddle。如果您可以调整您的要求,我将保留“扩展”的操作系统含义,使用Path.GetFileNameWithoutExension 摆脱扩展,然后以某种方式解析生成的字符串以查找末尾的(可选)索引

标签: c# regex


【解决方案1】:

你可以使用

^(?<base>.+?)\s*(?:\((?<idx>\d+)\))?(?<ext>\.[\w.]+)?$

查看regex demo,结果:

模式详情

  • ^ - 字符串开头
  • (?&lt;base&gt;.+?) - 组“base”:除换行符之外的任何 1 个或多个字符,尽可能少
  • \s* - 0+ 个空格
  • (?:\((?&lt;idx&gt;\d+)\))? - 可选序列:
    • \( - 一个 ( 字符
    • (?&lt;idx&gt;\d+) - 组“idx”:1+ 位
    • \) - 一个 ) 字符
  • (?&lt;ext&gt;\.[\w.]+)? - - 可选组“ext”:
    • \. - 一个 . 字符
    • [\w.]+ - 1+ 个字母、数字、_. 字符
  • $ - 字符串结束。

【讨论】:

    【解决方案2】:

    您可能要做的是重复() 部分,其中包含断言存在下一对的数字。然后将下一部分的数字捕获为idx 组。

    使用问号将 idx 组和 ext 组设为可选。

    ^(?<base>[^\r\n.()]+(?:(?:\(\d+\))*(?=\(\d+\)))?)(?:\((?<idx>\d+)\))?(?<ext>(?:\.[\w\.]+))?$
    
    • ^ 字符串开始
    • (?&lt;base&gt;开始base
      • [^\r\n.()]+ 匹配除所列字符之外的任何字符 1 次以上
      • (?:非捕获组
        • (?:\(\d+\))*(?=\(\d+\)) 重复匹配 (digits) 直到右边有 1 个 (digits) 部分
      • )? 关闭群组并将其设为可选
    • )结束base
    • (?:\((?&lt;idx&gt;\d+)\))?() 之间匹配idx 组的可选部分
    • (?&lt;ext&gt;(?:\.[\w\.]+))? 可选ext
    • $字符串结束

    Regex demo

    【讨论】:

    • 感谢您提供非常详细的答案。我选择了 Wictor 的回答思想,因为它更简单一些
    • @SteveB 没关系!
    猜你喜欢
    • 2011-05-18
    • 2017-01-10
    • 2014-08-08
    • 1970-01-01
    • 2020-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多