代码之家  ›  专栏  ›  技术社区  ›  Robert Long

按顺序提取子字符串

  •  2
  • Robert Long  · 技术社区  · 7 年前

    我有类似的字符串:

    the.string <- "982y987r0jhABCdioy2093uiwhf"
    

    我还有一个这样的子字符串向量:

    the.substrings <- c("ABC", "DEF", "GHI", "987")
    

    我想形成一个新的向量,只包含 the.substrings 发生在 the.string ,按照它们发生的顺序。所以在上面的例子中,我们只想 "987" "ABC" 按这样的顺序。

    我用以下算法实现了这一点:

    1. 循环每个 子串 并搜索每一个事件。
    2. 如果发生子字符串,请保存子字符串及其发生位置。
    3. 退出循环时,使用步骤2中保存的位置来排序出现的位置:

    mod.str <- list(2)
    pos.str <- numeric(2)
    n <- 1
    
    for (i in 1:length(the.substrings)) {
      reg.search <- gregexpr(the.substrings[i], the.string)
      if(reg.search[[1]][1] > 0) {
        mod.str[n] <- the.substrings[i]
        pos.str[n] <- reg.search[[1]][1]
        n <- n + 1
      }
    }
    
    dtfoo <- as.data.frame(cbind(mod.str, pos.str))
    dtfoo <- as.data.frame(lapply(dtfoo, unlist))
    
    as.character(dtfoo[order(dtfoo$pos.str),][, 1])
    

    这似乎行得通:

    [1] "987" "ABC"
    

    然而,我想知道是否有一种更好的(更有效,更少出错,也许使用函数式编程方法)来实现这一点?

    3 回复  |  直到 7 年前
        1
  •  1
  •   Wiktor Stribiżew    7 年前

    您可以使用以下基本R解决方案:

    regmatches(the.string, gregexpr(paste(the.substrings, collapse="|"), the.string))
    

    重点是你使用 the.substrings 构建包含 选择 加入一个 | alternation operator regmatches / gregexpr 将从左到右按照模式的显示顺序提取输入中出现的所有模式。

    图案看起来像 ABC|DEF|GHI|987|ABCDE 如果 子串 c("ABC", "DEF", "GHI", "987", "ABCDE") . 因为Regex引擎 格雷格斯珀 呼叫是 特雷 ,交替模式按照中描述的方式匹配 Text-Directed Engine Returns the Longest Match section at regular-expressions.info :

    当文本导向引擎尝试 Get|GetValue|Set|SetValue SetValue 它在字符串开始处尝试regex的所有排列。这样做效率很高,没有任何回溯。它看到regex可以在字符串的开头找到匹配项,匹配的文本可以是 Set 赋值 . 因为文本导向引擎将regex作为一个整体进行评估,所以它没有将一个选项列在另一个选项之前的概念。但它必须做出选择,选择哪一个匹配返回。它总是返回最长的匹配,在这种情况下 赋值 .

    如果使用相同的方法 paste(the.substrings, collapse="|") 使用 stringr::str_extract_all 您最终可能会得到一组不同的匹配,因为在ICU regex引擎中,会按照 请记住,regex引擎非常渴望 部分。主要的一点是,当找到一个匹配的替代方案时,其余的(在右边)甚至都没有尝试过。如果运行以下代码,您很容易看到:

    > the.string <- "ABCDE982y987r0jhABCdioy2093uiwhf"
    > the.substrings <- c("ABC", "DEF", "GHI", "987", "ABCDE")
    > str_extract_all(the.string, str_c(the.substrings, collapse = "|"))
    [[1]]
    [1] "ABC" "987" "ABC"
    
    > regmatches(the.string, gregexpr(paste(the.substrings, collapse="|"), the.string))
    [[1]]
    [1] "ABCDE" "987"   "ABC"  
    

    自从 ABC 是以前 ABCDE , 字符串::str_extract_all 返回 基础知识 比赛(比赛) DE 不匹配任何备选方案,将被跳过),并且 格雷格斯珀 检查所有可能的匹配项并返回最长的, ABCDE .

    另外,请参见 online R demo .

        2
  •  2
  •   kath    7 年前

    您可以使用 stringr 这样地:

    library(stringr)
    

    首先提取匹配字符串的位置

    string.locations <- str_locate(the.string, the.substrings)
    string.locations 
    #      start end
    # [1,]    12  14
    # [2,]    NA  NA
    # [3,]    NA  NA
    # [4,]     5   7
    

    按起始点排序,只提取前两个:

    string.locations <- string.locations[order(string.locations[, 1]), ]
    string.locations.sub <- string.locations[1:2, ]
    string.locations.sub 
    #      start end
    # [1,]     5   7
    # [2,]    12  14
    

    然后将原始字符串的子集设置为这些位置:

    str_sub(the.string, string.locations.sub)
    # [1] "987" "ABC"
    
        3
  •  0
  •   s_baldur    7 年前

    还使用 stringr :

    library(stringr)
    str_extract_all(the.string, str_c(the.substrings, collapse = "|"))[[1]][1:2]
    [1] "987" "ABC"