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.
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)
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? :)
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"
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.
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"
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)