【问题标题】:Efficient extraction of number in middle of semi-irregular text string半不规则字符串中间数字的高效提取
【发布时间】:2021-10-16 01:17:48
【问题描述】:

我想从一个文本字符串的中间提取一个有小变化的数字。所需数字之前的字符数有时为 4 长,有时为 5 长。有时在所需数字后面有一个“[字母].docx”,而其他时候只有一个“.docx”。

我已经写了一个蛮力解决方案,但我想学习如何更优雅地做到这一点,有两个具体问题。

两个问题:

  1. 如何更一般地编写下面的正则表达式语言?在我的情况下,我能够蛮力,因为我只有十个变体,但我希望看到一个通用的解决方案。
  2. 为什么 array() 选项不起作用?我正在尝试实现我所理解的描述here。在我的例子中,出于某种原因,R 在替换数组的第三个元素之后返回错误。

数据:

data$file
XX12_1a.docx
XX4_1b.docx
XX35_4.docx
XX9_3.docx
XX21_2.docx

目标:

data$id
1
1
4
3
2

SSCCE:

require('tidyverse')

data <- data.frame(file = c('XX12_1a.docx',
               'XX4_1b.docx',
               'XX35_4.docx',
               'XX9_3.docx',
               'XX21_2.docx'))

# Brute force solution:
data$id <- str_replace(data$file, '.....1a.....', '1')
data$id <- str_replace(data$id, '.....1b.....', '1')
data$id <- str_replace(data$id, '.....2.....', '2')
data$id <- str_replace(data$id, '.....3.....', '3')
data$id <- str_replace(data$id, '.....4.....', '4')
data$id <- str_replace(data$id, '....1a.....', '1')
data$id <- str_replace(data$id, '....1b.....', '1')
data$id <- str_replace(data$id, '....2.....', '2')
data$id <- str_replace(data$id, '....3.....', '3')
data$id <- str_replace(data$id, '....4.....', '4')

# More concise attempt, does not run
data$id2 <- str_replace(data$file, 
            array('.....1a.....', 
                  '.....1b.....', 
                  '.....2.....', 
                  '.....3.....',
                  '.....4.....',
                  '....1a.....',
                  '....1b.....',
                  '....2.....',
                  '....3.....',
                  '....4.....'), 
            array('1', '1', '2', '3', '4', '1', '1', '2', '3', '4'))

【问题讨论】:

    标签: r regex stringr


    【解决方案1】:

    由于目标数字是,从您的示例中看起来,总是前面是_,您可以使用lookbehind:

    library(stringr)
    str_extract(data$file, "(?<=_)\\d")
    

    【讨论】:

      【解决方案2】:

      你可以在这里使用sub

      data <- data.frame(file=c("XX12_1a.docx", "XX4_1b.docx", "XX35_4.docx", "XX9_3.docx", "XX21_2.docx"))
      data$id <- sub("^.*_(\\d+).*$", "\\1", data$file)
      data
      
                file id
      1 XX12_1a.docx  1
      2  XX4_1b.docx  1
      3  XX35_4.docx  4
      4   XX9_3.docx  3
      5  XX21_2.docx  2
      

      【讨论】:

        【解决方案3】:

        你可以使用extract:

        library(tidyverse)
        data <- data %>%
           extract(file, 'id', '_(\\d+)', remove = FALSE)
                  file id
        1 XX12_1a.docx  1
        2  XX4_1b.docx  1
        3  XX35_4.docx  4
        4   XX9_3.docx  3
        5  XX21_2.docx  2
        

        【讨论】:

          【解决方案4】:

          trimws 的选项来自base R

          data$id <- trimws(data$file, whitespace = ".*_|\\D?\\..*")
          

          -输出

          > data
                    file id
          1 XX12_1a.docx  1
          2  XX4_1b.docx  1
          3  XX35_4.docx  4
          4   XX9_3.docx  3
          5  XX21_2.docx  2
          

          数据

          data <- structure(list(file = c("XX12_1a.docx", "XX4_1b.docx", "XX35_4.docx", 
          "XX9_3.docx", "XX21_2.docx")), class = "data.frame", row.names = c(NA, 
          -5L))
          

          【讨论】:

            【解决方案5】:

            这是一个 tidyverse 解决方案:

            library(tidyverse)
            data %>% 
              separate(file, c("split1", "split2"), remove=FALSE) %>% 
              mutate(id = parse_number(split2), .keep="unused") %>% 
              select(-split1)
            

            输出:

                      file id
            1 XX12_1a.docx  1
            2  XX4_1b.docx  1
            3  XX35_4.docx  4
            4   XX9_3.docx  3
            5  XX21_2.docx  2
            

            【讨论】:

              猜你喜欢
              • 2019-05-21
              • 2012-08-19
              • 2021-01-23
              • 1970-01-01
              • 1970-01-01
              • 2015-08-16
              • 1970-01-01
              • 2012-06-24
              • 1970-01-01
              相关资源
              最近更新 更多