web-dev-qa-db-fra.com

Insérer un caractère à un emplacement spécifique dans une chaîne

Je voudrais insérer un caractère supplémentaire (ou une nouvelle chaîne) à un emplacement spécifique dans une chaîne. Par exemple, je veux insérer d au quatrième emplacement de abcefg pour obtenir abcdefg.

Maintenant j'utilise: 

old <- "abcefg"
n <- 4
paste(substr(old, 1, n-1), "d", substr(old, n, nchar(old)), sep = "")

Je pourrais écrire une fonction simple à une ligne pour cette tâche, mais je suis simplement curieux de savoir s’il existe une fonction pour cela.

42
Patrick Li

Vous pouvez le faire avec des expressions régulières et gsub.

gsub('^([a-z]{3})([a-z]+)$', '\\1d\\2', old)
# [1] "abcdefg"

Si vous souhaitez effectuer cette opération de manière dynamique, vous pouvez créer les expressions à l'aide de paste:

letter <- 'd'
lhs <- paste0('^([a-z]{', n-1, '})([a-z]+)$')
rhs <- paste0('\\1', letter, '\\2')
gsub(lhs, rhs, old)
# [1] "abcdefg"

selon le commentaire de DWin, vous voudrez peut-être que ce soit plus général.

gsub('^(.{3})(.*)$', '\\1d\\2', old)

De cette façon, tous les caractères seront identiques plutôt que minuscules. DWin suggère également d'utiliser sub au lieu de gsub. De cette façon, vous n'avez pas à vous soucier autant du ^ puisque sub ne correspondra qu'à la première instance. Mais j'aime être explicite dans les expressions régulières et ne passer qu'à des expressions plus générales telles que je les comprends et trouvant le besoin d'une plus grande généralité.


comme l'a noté Greg Snow, vous pouvez utiliser une autre forme d'expression régulière qui regarde derrière les correspondances:

sub( '(?<=.{3})', 'd', old, Perl=TRUE )

et pourrait également construire ma dynamique gsub ci-dessus en utilisant sprintf plutôt que paste0:

lhs <- sprintf('^([a-z]{%d})([a-z]+)$', n-1) 

ou pour son expression régulière sub:

lhs <- sprintf('(?<=.{%d})',n-1)
50
Justin

stringi package for the rescue encore une fois! La solution la plus simple et élégante parmi celles présentées.

La fonction stri_sub vous permet d'extraire des parties de la chaîne et de les remplacer comme suit:

x <- "abcde"
stri_sub(x, 1, 3) # from first to third character
# [1] "abc"
stri_sub(x, 1, 3) <- 1 # substitute from first to third character
x
# [1] "1de"

Mais si vous faites ceci:

x <- "abcde"
stri_sub(x, 3, 2) # from 3 to 2 so... zero ?
# [1] ""
stri_sub(x, 3, 2) <- 1 # substitute from 3 to 2 ... hmm
x
# [1] "ab1cde"

alors aucun caractère n'est supprimé mais un nouveau est inséré. N'est-ce pas cool? :)

14
bartektartanus

La réponse de @ Justin est la façon dont je l'aborderais en raison de sa flexibilité, mais ceci pourrait aussi être une approche amusante.

Vous pouvez traiter la chaîne comme "format de largeur fixe" et spécifier l'endroit où vous souhaitez insérer votre caractère:

paste(read.fwf(textConnection(old), 
               c(4, nchar(old)), as.is = TRUE), 
      collapse = "d")

Lorsque vous utilisez sapply, la sortie est particulièrement agréable, car vous pouvez voir la chaîne d'origine sous le nom "name".

newold <- c("some", "random", "words", "strung", "together")
sapply(newold, function(x) paste(read.fwf(textConnection(x), 
                                          c(4, nchar(x)), as.is = TRUE), 
                                 collapse = "-WEE-"))
#            some          random           words          strung        together 
#   "some-WEE-NA"   "Rand-WEE-om"    "Word-WEE-s"   "stru-WEE-ng" "toge-WEE-ther" 
8

Votre façon originale de procéder (diviser la chaîne à un index et coller dans le texte inséré) pourrait être transformée en une fonction générique, comme ceci:

split_str_by_index <- function(target, index) {
  index <- sort(index)
  substr(rep(target, length(index) + 1),
         start = c(1, index),
         stop = c(index -1, nchar(target)))
}

#Taken from https://stat.ethz.ch/pipermail/r-help/2006-March/101023.html
interleave <- function(v1,v2)
{
  ord1 <- 2*(1:length(v1))-1
  ord2 <- 2*(1:length(v2))
  c(v1,v2)[order(c(ord1,ord2))]
}

insert_str <- function(target, insert, index) {
  insert <- insert[order(index)]
  index <- sort(index)
  paste(interleave(split_str_by_index(target, index), insert), collapse="")
}

Exemple d'utilisation: 

> insert_str("1234567890", c("a", "b", "c"), c(5, 9, 3))
[1] "12c34a5678b90"

Cela vous permet d'insérer un vecteur de caractères aux emplacements indiqués par un vecteur d'index. Les fonctions split_str_by_index et interleave sont également utiles en elles-mêmes. 

Modifier:  

J'ai révisé le code pour autoriser les index dans n'importe quel ordre. Auparavant, les index devaient être dans l'ordre croissant.

3
Zach Foster

J'ai créé une fonction personnalisée appelée substr1 pour traiter l'extraction, le remplacement et l'insertion de caractères dans une chaîne. Exécutez ces codes au début de chaque session. N'hésitez pas à l'essayer et laissez-moi savoir s'il doit être amélioré.

# extraction
substr1 <- function(x,y) {
  z <- sapply(strsplit(as.character(x),''),function(w) paste(na.omit(w[y]),collapse=''))
  dim(z) <- dim(x)
  return(z) }

# substitution + insertion
`substr1<-` <- function(x,y,value) {
  names(y) <- c(value,rep('',length(y)-length(value)))
  z <- sapply(strsplit(as.character(x),''),function(w) {
    v <- seq(w)
    names(v) <- w
    paste(names(sort(c(y,v[setdiff(v,y)]))),collapse='') })
  dim(z) <- dim(x)
  return(z) }

# demonstration
abc <- 'abc'
substr1(abc,1)
# "a"
substr1(abc,c(1,3))
# "ac"
substr1(abc,-1)
# "bc"
substr1(abc,1) <- 'A'
# "Abc"
substr1(abc,1.5) <- 'A'
# "aAbc"
substr1(abc,c(0.5,2,3)) <- c('A','B')
# "AaB"
0
dasf

Il m'a fallu du temps pour comprendre l'expression habituelle, puis j'ai trouvé mon chemin avec les chiffres 

Le résultat final était 

old <- "89580000"
gsub('^([0-9]{5})([0-9]+)$', '\\1-\\2', old)
0
Joni Hoppen