代码之家  ›  专栏  ›  技术社区  ›  Josh

从路径中提取文件夹、文件名和扩展名

  •  0
  • Josh  · 技术社区  · 8 年前

    我有一个包含文件路径和名称的数据框,格式如下:

    files_list <- c(
      "C:/User/Name/Folder/Subfolder1/Sub-subfolder/file.txt", 
      "C:/User/Name/Folder/Subfolder1/Sub-subfolder/file - Copy.txt",
      "C:/User/Name/Folder/Subfolder1/Sub-subfolder/file (1).txt",
      "C:/User/Name/Folder/Subfolder1/Sub-subfolder/file - Copy (2).txt",
      "C:/User/Name/Folder/Subfolder1/fileB.txt",
      "C:/User/Name/Folder/file.C.txt",
      "C:/User/Name/Folder/file-D.txt", 
      "C:/User/Name/Folder/file",
      "C:/User/Name/Folder/file Z.txt", 
      "C:/User/Name/Folder/file - backup.txt"
    )
    

    每个文件都有父文件夹和名称。这些名称可以包含一个或多个句点“.”和/或破折号“-”。此外,有些还有“副本”符号、数字名称和/或文件扩展名。我要将数据转换为如下所示的内容:

    [1]  "Sub-subfolder   file   txt"
    [2]  "Sub-subfolder   file   Copy   txt"
    [3]  "Sub-subfolder   file   1   txt"
    [4]  "Sub-subfolder   file   Copy   2   txt"
    [5]  "Subfolder1   fileB   txt"
    [6]  "Folder   file.C   txt"
    [7]  "Folder   file-D   txt"
    [8]  "Folder   file"
    [9]  "Folder   file Z   txt"
    [10] "Folder   file - backup   txt"
    

    这是我认为应该做到的代码:

    sub(
      "(^.:/)([^/.]+/)*([^/.]+/)([^/]+)(\\s-\\sCopy)?(\\s\\(([0-9]+)\\))?(\\.([^.]+))?$", 
      "\\3   \\4   \\5   \\7   \\9",
      files_list
    )
    

    但我得到的是:

    [1] "Sub-subfolder/   file.txt         "           
    [2] "Sub-subfolder/   file - Copy.txt         "    
    [3] "Sub-subfolder/   file (1).txt         "       
    [4] "Sub-subfolder/   file - Copy (2).txt         "
    [5] "Subfolder1/   fileB.txt         "             
    [6] "Folder/   file.C.txt         "                
    [7] "Folder/   file-D.txt         "                
    

    斜杠“/”和额外的空格我可以处理,但“复制”符号、数字名称和文件扩展名并没有像我期望的那样分开。

    关于如何识别“复制”符号、编号和文件扩展名有何建议?或者我应该在一行代码中标识父文件夹,在另一行代码中分隔其余文件夹?

    (最终,我将把这些文本字符串转换成一个数据框,其中文件夹、文件名、副本名称和扩展名是单独的列。我很确定我可以用它 tidyr::separate ,但即使这样也需要了解regex,我想学习如何使用 () 以及后面的参考资料。)

    3 回复  |  直到 8 年前
        1
  •  2
  •   RHertel    8 年前

    这可能有助于:

    library(tools)
    as.data.frame(cbind(dirname(files_list), file_path_sans_ext(basename(files_list)), file_ext(files_list)))
    #                                            V1              V2  V3
    #1 C:/User/Name/Folder/Subfolder1/Sub-subfolder            file txt
    #2 C:/User/Name/Folder/Subfolder1/Sub-subfolder     file - Copy txt
    #3 C:/User/Name/Folder/Subfolder1/Sub-subfolder        file (1) txt
    #4 C:/User/Name/Folder/Subfolder1/Sub-subfolder file - Copy (2) txt
    #5               C:/User/Name/Folder/Subfolder1           fileB txt
    #6                          C:/User/Name/Folder          file.C txt
    #7                          C:/User/Name/Folder          file-D txt
    #8                          C:/User/Name/Folder            file    
    
        2
  •  1
  •   Onyambu    8 年前

    我仍然不知道你是否需要把它们串成一根绳子:如下所示

    gsub("[/().]| - "," ",sub(".*?([^/]+/[^/]+$)","\\1",files_list))
    
    [1] "Sub-subfolder file txt"         
    [2] "Sub-subfolder file Copy txt"    
    [3] "Sub-subfolder file  1  txt"     
    [4] "Sub-subfolder file Copy  2  txt"
    [5] "Subfolder1 fileB txt"           
    [6] "Folder file C txt"              
    [7] "Folder file-D txt"              
    [8] "Folder file"  
    

    如果您只需要一种模式,那么:

    pattern="[^/]+(?=/[^/]+$)|\\w+(?=[ ).-])|\\w+$"
    regmatches(files_list,gregexpr(pattern,files_list,perl = TRUE))
    

    Demo

        3
  •  0
  •   Josh    8 年前

    抱歉,如果这不是最好的方法。我意识到我的问题是不完整的,我想让这个问题更完整,同时也分享我提出的解决方案。

    我希望此代码处理所有可能的名称结构:

    1. “c:/”或任何其他目录/子目录中的文件
    2. 具有以下任何字符/功能的文件名
      • 文件扩展名开头“.”之前的“.”
      • “-”或“-”不是“-副本”的一部分
      • 文件名末尾的“”或“(”不是“(数字)”的一部分

    我使用此代码生成示例文件名/路径覆盖所有文件夹/名称/副本/编号/扩展名组合:

    files.df <- expand.grid(
        c("C:/"), 
        c("", "F1/", "F1/F2/"), 
        c("folder/"), 
        c("file"), 
        c("", " space", "-dash", " - spacedash", ".period", ".firstperiod.secondperiod"), 
        c("", 1, " 1", 10, " 10"), 
        c("", " - Copy"), 
        c("", " (1)", " (10)"), 
        c("", ".999", ".aaa"), 
        stringsAsFactors = F
    )
    
    for (i in 1:nrow(files.df)) {
        if (!exists("x")) {
            x <- vector(mode="character", length=0)
        }
        x[i] <- paste(as.character(as.vector(files.df[i, ])), sep = "", collapse = "")
    }
    

    经过多次试错使用( regex101 ,谢谢@onyambu!),我把下面的荒谬正则表达式组合在一起,它们实际上是有效的:

    sum(grepl(
        "^.:/(([^/]+)(?=/)/?)*(?<=/)(([^/](?! - Copy| \\([0-9]+\\)|\\.[^/\\.]+$))+.)( - )?((?<= - )Copy(?= \\([0-9]+\\)(?=\\.[^/\\.]+$|$)|\\.[^/\\.]+$|$))?( \\()?((?<= \\()([0-9]+)\\)(?=\\.[^/\\.]+$|$))?\\.?((?<=\\.)([^/\\.]+))?$", 
        x,
        perl = T
    ))
    [1] 1620
    
    length(x)
    [1] 1620
    

    不幸的是,这个regex包含10个捕获组,我只能反向引用其中的9个(并且10是文件扩展名)。所以我将使用@rhertel更优雅的解决方案。但如果有人看到减少抓捕组数量的方法,请告诉我!