Как заменить совпадения в строке и индексировать каждое совпадение

Конкретная строка может содержать несколько экземпляров шаблона, который я пытаюсь сопоставить. Например, если мой шаблон <N(.+?)N> и моя строка "My name is <N Timon N> and his name is <N Pumba N>"Тогда есть два матча. Я хочу заменить каждое совпадение заменой, содержащей индекс, для которого заменяется совпадение.

Так в моей строке "My name is <N Timon N> and his name is <N Pumba N>"Я хочу изменить строку для чтения "My name is [Name #1] and his name is [Name #2]",

Как мне это сделать, желательно с помощью одной функции? И желательно с использованием функций из stringr или же stringi?

4 ответа

Решение

Вы можете сделать это с gregexpr а также regmatches в базе R:

my_string = "My name is <N Timon N> and his name is <N Pumba N>"

# Get the positions of the matches in the string
m = gregexpr("<N(.+?)N>", my_string, perl = TRUE)

# Index each match and replace text using the indices
match_indices = 1:length(unlist(m))

regmatches(my_string, m) = list(paste0("[Name #", match_indices, "]"))

Результат:

> my_string
# [1] "My name is [Name #1] and his name is [Name #2]"

Замечания:

Это решение обрабатывает одно и то же совпадение как другое "Имя", если оно появляется более одного раза. Например следующее:

my_string = "My name is <N Timon N> and his name is <N Pumba N>, <N Timon N> again"


m = gregexpr("<N(.+?)N>", my_string, perl = TRUE)

match_indices = 1:length(unlist(m))

regmatches(my_string, m) = list(paste0("[Name #", match_indices, "]"))

выходы:

> my_string
[1] "My name is [Name #1] and his name is [Name #2], [Name #3] again"

Вот решение, которое опирается на gsubfn а также proto пакеты.

# Define the string to which the function will be applied
my_string <- "My name is <N Timon N> and his name is <N Pumba N>"

# Define the replacement function
replacement_fn <- function(x) {

  replacment_proto_fn <- proto::proto(fun = function(this, x) {
      paste0("[Name #", count, "]")
  })

  gsubfn::gsubfn(pattern = "<N(.+?)N>",
                 replacement = replacment_proto_fn,
                 x = x)
}

# Use the function on the string
replacement_fn(my_string)

Вот другой подход с dplyr + stringr:

library(dplyr)
library(stringr)

string %>%
  str_extract_all("<N(.+?)N>") %>%
  unlist() %>%
  setNames(paste0("[Name #", 1:length(.), "]"), .) %>%
  str_replace_all(string, .)

# [1] "My name is [Name #1] and his name is [Name #2]"

Замечания:

Второе решение извлекает совпадения с str_extract_allзатем использует совпадения для создания именованного вектора замен, который в итоге подается в str_replace_all искать и заменять соответственно.

Как указывает OP, это решение дает результаты, отличные от gregexpr + regmatches подход в некоторых случаях. Например следующее:

string = "My name is <N Timon N> and his name is <N Pumba N>, <N Timon N> again"

string %>%
  str_extract_all("<N(.+?)N>") %>%
  unlist() %>%
  setNames(paste0("[Name #", 1:length(.), "]"), .) %>%
  str_replace_all(string, .)

выходы:

[1] "My name is [Name #1] and his name is [Name #2], [Name #1] again"

Просто, может быть медленно, но должно работать:

ct <- 1
while(TRUE) {
 old_string <- my_string; 
 my_string <- stri_replace_first_regex(my_string, '\\<N.*?N\\>', 
       paste0('[name', ct, ,']')); 
  if (old_string == my_string) break 
  ct <- ct + 1
}
Другие вопросы по тегам