【问题标题】:Regex exercise in RR中的正则表达式练习
【发布时间】:2017-12-09 03:05:11
【问题描述】:

我想用 R 解析一个字符串,我想得到一个对象列表。字符串中的括号、空格和逗号决定了最终列表的结构:

  1. 每对括号之间用空格隔开,每对括号中的单词必须组成一个新的列表对象;

  2. 括号中的单词以逗号分隔,并应在每个列出的对象中形成不同的元素;

  3. 上述结构也可以嵌套在一对括号内。

这里是字符串的一个例子:

x <- "(K01596,K01610) (K01689) (K01834,K15633,K15634,K15635) (K00927) (K00134,K00150) (K01803) ((K01623,K01624,K11645) (K03841,K02446,K11532,K01086,K04041),K01622)"

所需的输出应该是这样的:

list(c("K01596","K01610"), "K01689", c("K01834","K15633","K15634","K15635"), "K00927", c("K00134","K00150"), "K01803", list(list(c("K01623","K01624","K11645"), c("K03841","K02446","K11532","K01086","K04041")), "K01622"))

我设法解决了如何对案例 1) 进行解析

match <- gregexpr("\\((?>[^()]|(?R))*\\)", x, perl = T)
x2 <- as.list(substring(x, match[[1]], match[[1]] + attr(match[[1]], "match.length") - 1))

和案例 2) 也很简单,我可以用 gsub 删除括号并使用 strsplit 拆分单词。 问题是如何解析案例 3),当我有一个嵌套级别时:

((K01623,K01624,K11645) (K03841,K02446,K11532,K01086,K04041),K01622)

我必须取出一个列表对象,它本身就是一个列表:

list(list(c("K01623","K01624","K11645"), c("K03841","K02446","K11532","K01086","K04041")), "K01622")

【问题讨论】:

  • 1.,2.,3.可以更清楚地重述为 “1. 括号包围列表 2. 逗号分隔单个项目 3. 列表可以嵌套”
  • 您也没有提到单个列表项的格式,但它看起来像 " 字母后跟几个 (5?) 数字,例如 K01596"

标签: r regex string parsing


【解决方案1】:

可以转成JSON,然后用jsonlite转成列表。拥有此功能后,您可以随意简化、折叠或重新组织您的列表。

library(jsonlite)
library(stringr)

add_paren <- function(x){
  x <- str_sub(x, end = -2) #remove comma
  paste0("(", x, "), ") #add enclosing paren and return comma
} 
x <- str_replace_all(x, "\\(\\(.*\\)\\,", add_paren)

x <- gsub("\\(", "\\[", x)
x <- gsub("\\)", "\\]", x)
x <- gsub("\\] \\[", "\\], \\[", x)

add_quote <- function(x) paste0('"', x, '"')

x <- str_replace_all(x, "K[0-9]*", add_quote)
x <- paste0("[", x, "]")

x2 <- fromJSON(x)

导致:

dput(x2)

list(c("K01596", "K01610"), "K01689", c("K01834", "K15633", "K15634", 
"K15635"), "K00927", c("K00134", "K00150"), "K01803", list(list(
    c("K01623", "K01624", "K11645"), c("K03841", "K02446", "K11532", 
    "K01086", "K04041")), "K01622"))

str(x2)

List of 7
 $ : chr [1:2] "K01596" "K01610"
 $ : chr "K01689"
 $ : chr [1:4] "K01834" "K15633" "K15634" "K15635"
 $ : chr "K00927"
 $ : chr [1:2] "K00134" "K00150"
 $ : chr "K01803"
 $ :List of 2
  ..$ :List of 2
  .. ..$ : chr [1:3] "K01623" "K01624" "K11645"
  .. ..$ : chr [1:5] "K03841" "K02446" "K11532" "K01086" ...
  ..$ : chr "K01622"

【讨论】:

  • 我正在走这条路,然后放弃了。不错的解决方案
  • 我不熟悉 json 格式,但似乎很适合我的问题!但是,如果可能的话,我希望列表的第 7 个元素具有不同的结构;如果你运行我在问题中发布的最后一段代码,你会明白我的意思
  • 啊,没听懂。我添加了一行 x &lt;- str_replace_all(x, "\\(\\(.*\\)\\,", add_paren),它应该确保任何匹配模式 ((.*) 的东西都嵌套更深一层。这似乎给出了所需的输出。
【解决方案2】:

我建议您将已经为案例 1) 找到的正则表达式递归地应用于输入。也就是说,为找到的每个匹配项调用递归函数。

如果未找到匹配项,则属于情况 2),并且可以在输入上使用 strsplit。我在下面整理了一个示例函数:

constructList <- function(x) {

  matches <- gregexpr("\\((?>[^()]|(?R))*\\)", x, perl = T)

  if (matches[[1]][1] == -1) {
    return(strsplit(x, ",")[[1]])
  }

  lapply(
    lapply(1:length(matches[[1]]), function(i)
                                        substr(x,
                                               matches[[1]][i] + 1,
                                               matches[[1]][i] + attr(matches[[1]], "match.length")[i] - 2)),
    constructList)

}

输出似乎还可以:

constructList(x)
[[1]]
[1] "K01596" "K01610"

[[2]]
[1] "K01689"

[[3]]
[1] "K01834" "K15633" "K15634" "K15635"

[[4]]
[1] "K00927"

[[5]]
[1] "K00134" "K00150"

[[6]]
[1] "K01803"

[[7]]
[[7]][[1]]
[1] "K01623" "K01624" "K11645"

[[7]][[2]]
[1] "K03841" "K02446" "K11532" "K01086" "K04041"

【讨论】:

  • 不错的功能!但是嵌套部分仍然存在2个问题。最后一个术语缺少“K01622”,我希望案例 3 的结构类似于通过运行我在问题中发布的最后一段代码来查看。
猜你喜欢
  • 1970-01-01
  • 2010-09-05
  • 1970-01-01
  • 1970-01-01
  • 2021-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多