【问题标题】:How to fill gap between two characters with regex如何用正则表达式填充两个字符之间的空白
【发布时间】:2014-12-11 19:13:13
【问题描述】:

我有一个如下所示的数据集。我想用 1 替换两个 1 之间的所有点,如 desired.result 所示。我可以在基础R 中使用regex 执行此操作吗?

我试过了:

regexpr("^1\\.1$", my.data$my.string, perl = TRUE)

这是c#中的一个解决方案

Characters between two exact characters

感谢您的任何建议。

my.data <- read.table(text='
     my.string                           state
     ................1...............1.    A
     ......1..........................1    A
     .............1.....2..............    B
     ......1.................1...2.....    B
     ....1....2........................    B
     1...2.............................    C
     ..........1....................1..    C
     .1............................1...    C
     .................1...........1....    C
     ........1....2....................    C
     ......1........................1..    C
     ....1....1...2....................    D
     ......1....................1......    D
     .................1...2............    D
', header = TRUE, na.strings = 'NA', stringsAsFactors = FALSE)

desired.result <- read.table(text='
     my.string                           state
     ................11111111111111111.    A
     ......1111111111111111111111111111    A
     .............1.....2..............    B
     ......1111111111111111111...2.....    B
     ....1....2........................    B
     1...2.............................    C
     ..........1111111111111111111111..    C
     .111111111111111111111111111111...    C
     .................1111111111111....    C
     ........1....2....................    C
     ......11111111111111111111111111..    C
     ....111111...2....................    D
     ......1111111111111111111111......    D
     .................1...2............    D
', header = TRUE, na.strings = 'NA', stringsAsFactors = FALSE)

【问题讨论】:

  • 这种模式在regexpr:"1\\.*1" 中会给出更有用的结果。 ^$ 只允许在字符串的两端都有 1 的情况下进行匹配(并且缺少量词只会匹配只有 1 个句点的字符串。)
  • 四个(或更多)1 在同一行(即..1...1...1...1..)时的预期结果是什么? ..11111...11111....1111111111111..?

标签: regex r


【解决方案1】:

以下是使用gsub\G 功能和环视断言的选项。

> gsub('(?:1|\\G(?<!^))\\K\\.(?=\\.*1)', '1', my.data$my.string, perl = TRUE)
# [1] "................11111111111111111." "......1111111111111111111111111111"
# [3] ".............1.....2.............." "......1111111111111111111...2....."
# [5] "....1....2........................" "1...2............................."
# [7] "..........1111111111111111111111.." ".111111111111111111111111111111..."
# [9] ".................1111111111111...." "........1....2...................."
# [11] "......11111111111111111111111111.." "....111111...2...................."
# [13] "......1111111111111111111111......" ".................1...2............"

\G 特征是可以匹配两个位置之一的锚点;字符串的开始位置或最后一次匹配结束的位置。由于您似乎想避免字符串位置开头的点,我们使用环视断言\G(?&lt;!^) 来排除字符串的开头。

\K 转义序列会重置报告匹配的起点,并且不再包含任何以前使用的字符。

您可以找到解释正则表达式 here 的总体细分。

【讨论】:

  • 非凡。谢谢你。如果您有时间,请考虑发布有关其工作原理的说明。
  • 这是一个糟糕的正则表达式。 @MarkMiller,见here
  • 天啊!一个自动的正则表达式解释器?!?
  • @MarkMiller 这是经过测试的regex
  • 你可以像这样改进模式:(?:\G(?!^)|\.*1(?=\.+1))\K\.(前瞻只测试一次,\G 分支(最成功)在第一个位置。+ 量词不包括连续1\.* 避免在打开之前测试每个字符的两个分支1)
【解决方案2】:

使用gsubfn,第一个参数是一个正则表达式,它匹配1 和1 之间的字符并捕获后者。第二个参数是一个函数,用公式表示法表示,它使用gsub将捕获的字符串中的每个字符替换为1:

library(gsubfn)
transform(my.data, my.string = gsubfn("1(.*)1", ~ gsub(".", 1, x), my.string))

如果字符串中可以有多对 1,则使用 "1(.*?)1" 作为正则表达式。

可视化这里的正则表达式很简单,可以直接理解,但这里有一个调试表达式可视化:

1(.*)1

Debuggex Demo

【讨论】:

  • 感谢您添加此内容。我给了gsubfn() 一个快速的镜头(使用前瞻和后瞻断言),但被它的非 PCRE 正则表达式引擎所阻碍。我喜欢这个解决方案的干净程度。
【解决方案3】:

这是一个选项,它使用相对简单的正则表达式和gregexpr()regmatches()regmatches&lt;-() 的标准组合来识别、提取、操作并替换与该正则表达式匹配的子字符串。

## Copy the character vector
x <- my.data$my.string
## Find sequences of "."s bracketed on either end by a "1"
m <- gregexpr("(?<=1)\\.+(?=1)", x, perl=TRUE)
## Standard template for operating on and replacing matched substrings
regmatches(x,m) <- sapply(regmatches(x,m), function(X) gsub(".", "1", X))

## Check that it worked
head(x)
# [1] "................11111111111111111." "......1111111111111111111111111111"
# [3] ".............1.....2.............." "......1111111111111111111...2....."
# [5] "....1....2........................" "1...2............................."

【讨论】: