在 R 中,我在
data.frame
列中有一些格式相当不寻常的字符串,我想用逗号分隔它们(即,该列的每个元素都是用逗号分隔的单个值的字符串;这些字符串来自 postgresql
查询,但问题更普遍,由于各种原因我无法修复这是 postgresql
查询本身)。包含字符串的数据框 x
如下所示:
> x
v
1 {'a',b}
2 {"abc, def","the \\"big\\" one",fgh}
(注意:上面引用内容中的双斜杠
\\
只是转义实际字符串中的单个\
)
如果值本身包含空格、双引号或逗号,则用逗号分隔的值列表中的任何元素都会加上双引号,但如果值不使用这些特殊字符,则不使用双引号。单个值中的文字双引号表示为文字
\"
。这些特征使得用逗号分割值非常困难。
我想拆分这些值,使
{'a',b}
变为 c("'a'","b")
且 {"abc, def","the \\"big\\" one",fgh}
变为 c("abc, def","the \"big\" one","fgh")
。我已经找到了解决这个问题的办法,但它是“丑陋”的大写U,我想知道是否有更好的方法。我的解决方案是:
# Code to create data.frame using postgresql query
con <- DBI::dbConnect(RPostgres::Postgres())
q = "WITH a(v) AS (VALUES(ARRAY['''a''','b']::varchar[]),(ARRAY['abc, def','the \"big\" one','fgh']::varchar[])) SELECT * FROM a"
x <- DBI::dbGetQuery(con,q)
# Split quoted strings avoiding literal quotes within values
xx = gsub("[\\][\"]","%myspecialquote%",x$v) # Protect quoted quotes
y = stringi::stri_extract_all_regex(xx, '(?<=[{"]).*?(?=["}])')
yy = lapply(y,\(.) gsub("%myspecialquote%","\"",.)) # Put back quotes
z = lapply(yy,\(.) .[.!="," & .!=""]) # Remove empty strings and lone commas
# Split what wasn't split already
w = lapply(1:length(z),\(.) {
t = ifelse(grepl("\"",x$v[.]),z[.],strsplit(z[[.]],","))[[1]]
t = lapply(t,\(.) ifelse(grepl("^,",.) | grepl(",$",.),(gsub("^[,]|[,]$","",.) |> strsplit(","))[[1]],.))
return(t)
})
# Put humpty-dumpty back together again
v=sapply(w,\(.) {
if(is.list(.))
. = do.call(c,.)
return(.)
})
将所有
,
;
(如果它们未被
"
包围)
删除所有 {
}
。将 \\
\
沿;
分开删除尾随
"
library(stringr)
library(magrittr)
library(purrr)
(x <- data.frame(v = c("{'a',b}",
"{\"abc, def\",\"the \\\"big\\\" one\",fgh}")))
# v
# 1 {'a',b}
# 2 {"abc, def","the \\"big\\" one",fgh}
r <- x$v %>%
str_replace_all(',(?=(?:[^"]*"[^"]*")*[^"]*$)', ';') %>%
str_remove_all("[{}]") %>%
str_replace_all("\\\\", "\\") %>%
str_split(fixed(";")) %>%
map(~ str_remove_all(.x, '^"|"$'))
dput(r[[1]])
# c("'a'", "b")
dput(r[[2]])
# c("abc, def", "the \"big\" one", "fgh")
最困难的部分是
regex
:
,
(?=
(?:[^"]*"[^"]*")*
(?:
),匹配双引号对(跳过引号内的内容)。[^"]*$