【问题标题】:List duplicate names of .png file name to text file by skipping hash (#)通过跳过哈希 (#) 将 .png 文件名的重复名称列出到文本文件
【发布时间】:2022-01-15 05:12:52
【问题描述】:

我正在尝试检查如何检查所有具有相同名称的文件名,并希望将这些相似的文件名与文件夹路径分组并将它们导出到文本文件。

到目前为止,我已经编写了以下代码,可以让我在每个 .png 文件名中找到 # 并成功在 # 后面添加数字。但我无法通过跳过 # 和之后的数字来检查重复名称。

我所有的文件名都是 .png 格式。

我有超过 100 个子文件夹,每个文件夹都有超过 2 个文件,最多 25 个 .png 文件。

文件命名风格:

非常感谢任何建议!

 private void button1_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog folderDlg = new FolderBrowserDialog();
            folderDlg.SelectedPath = "C:\\Users\\demo\\Documents\\process\\";
            if (folderDlg.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            // Has different framework dependend implementations 
            // in order to handle unauthorized access to subfolders
            RenameAllPngFiles(folderDlg.SelectedPath);
        }
        private void RenameAllPngFiles(string directoryPath)
        {
            RenameCurrentPng(directoryPath);
            foreach (var item in GetDirectoryInfos(directoryPath))
            {
                RenameCurrentPng(item.FullName);
            }
        }

        private void RenameCurrentPng(string directoryPath)
        {
            int fileNameSuffixCounter = 1;
            foreach (string originalFullFileName in Directory.EnumerateFiles(directoryPath, "*.png"))
            {
                string ShortFileName = System.IO.Path.GetFileNameWithoutExtension(originalFullFileName);
                if (!ShortFileName.Contains("#"))
                {
                    // The new file name without path
                    var newFileName = $"{ShortFileName}#{fileNameSuffixCounter++}{System.IO.Path.GetExtension(originalFullFileName)}";
                    FileSystem.RenameFile(originalFullFileName, newFileName);
                }
            }
        }

        private DirectoryInfo[] GetDirectoryInfos(string directoryPath)
        {
            DirectoryInfo di = new DirectoryInfo(directoryPath);
            DirectoryInfo[] directories = di.GetDirectories("*", System.IO.SearchOption.AllDirectories);
            return directories;
    }

【问题讨论】:

  • 真的很难说出你在问什么。听起来您有诸如c:\img\monday\some#1.png c:\img\tuesday\some#1.png c:\img\wednesday\some#1.png 之类的文件,并且您想让文件名在所有子文件夹中都是唯一的,例如c:\img\monday\some#1.png c:\img\tuesday\some#2.png c:\img\wednesday\some#3.png - 对吗?如果不正确,您可以编辑您的问题,以便类似地提出您的问题吗?说出你拥有什么/你在哪里,以及你在这个过程结束时要拥有什么/你想成为什么?
  • @CaiusJard 嘿,谢谢您的回复。我想在所有文件夹中捕获重复的名称。但在我的文件名中,我得到了 xxx#1.png。我想在查找重复文件名时排除这个 # 和数字,然后将它们分组并列出/将名称列表处理为文本文件

标签: c# file-io duplicates filenames


【解决方案1】:

这将按 # 之前的名称片段对文件进行分组,并通过在所有子文件夹中添加唯一编号来建议新名称

    var fs = Directory.GetFiles("c:\\temp\\a", "*.*", SearchOption.AllDirectories);

    var changes = fs
        .GroupBy(f => Path.GetFileName(f).Split('#', 2)[0],
          (k, g) => g.Select((f, i) => new
          {
              OldPath = f,
              NewPath = Path.Combine(Path.GetDirectoryName(f), $"{k}#{i}{Path.GetExtension(f)}")
          })
        );

那么它有什么作用呢?

  • 枚举所有目录中的所有文件到fs
  • Group fs by filename.Split('#', 2)[0] 意思是“在#上拆分,最多返回2个子字符串,因为我们只需要第一个,把第一个作为要分组的东西”
  • 这会导致例如将所有不同的a#xx.txt 分组到一个列表中。 GroupBy 的第二个参数就像对分组结果运行一个选择(所有文件路径的“数组”,名称以a 开头)
    • k 是键,即a#1.txt 中的a'b#1.txt 中的b
    • g 是完整文件路径的组(数组)
  • 这意味着我们可以在g 上运行Select 并使用Select((item, index_of_item) 的重载
    • f 是项目,即完整路径,
    • i 是“数组”中的索引,即该文件名的唯一编号。
  • 使用它,我们将创建一个新对象,即
    • OldPath 的原始完整路径 f,以及
    • NewPath 即“f的目录,加上第一个#之前的所有组的键,加上一个#,加上唯一编号i,加上f的扩展名

我想,如果您想运行一些重命名,将它们作为旧/新名称对的列表会很方便。还有一些其他的事情也可能是明智的;有一个临时的、保证唯一的文件名,我们首先将其移至:

var flatListWithTempName = changes.SelectMany(x => x, x =>
  new { 
    x.OldPath,
    x.NewPath, 
    TempPath = x.OldPath + Guid.NewGuid()
  }
).ToArray();

foreach(var change in flatListWithTempName){
  File.Move(change.OldPath, change.TempPath);
}

foreach(var change in flatListWithTempName){
  File.Move(change.TempPath, change.NewPath);
}

SelectMany 将展开一层嵌套。第二个参数使用新的 guid 重塑旧/新的临时路径。为什么要这样做?

好吧,假设由于某种原因,您的重命名之一是 a#1.txt -> a#10.txt,但该文件夹已包含 a#10.txt,您会遇到问题。通过遍历一个唯一的文件名,您可以避免任何冲突。通过在名称上添加 guid,我们使其独一无二,并且如果程序崩溃而工作完成一半,则可以轻松撤消最后的 guid(修剪最后 36 个字符)


如果对您有帮助,请使用稍长的变量名:

var fullPathsOfAllFiles = Directory.GetFiles("c:\\temp\\a", "*.*", SearchOption.AllDirectories);

var changes = fullPathsOfAllFiles 
        .GroupBy(fullPathOfOneFile => Path.GetFileName(fullPathOfOneFile).Split('#', 2)[0],
          (partialNameBeforeHash, fullPathsWithThatPartial) => arrayOfFullPathsWithThatPartial.Select((fullPath, arrayIndex) => new
          {
              OldPath = fullPath,
              NewPath = Path.Combine(Path.GetDirectoryName(fullPath), $"{partialNameBeforeHash}#{arrayIndex}{Path.GetExtension(fullPath)}")
          })
        );

我说“数组”,因为它的行为类似于一个用于这些目的,但它实际上是一个可枚举


这是一个名称重复的文件结构:

这是分组结果的内容。您可以看到 NewPath 包含所有重新编号的文件,从 0 开始,并且所有文件名都是唯一的:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 2018-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多