Une chaîne de caractères est un morceau de texte placé entre guillemets.
mystring <- "some text"
mystring
#> [1] "some text"
typeof(mystring)
#> [1] "character"
Malheureusement, cette terminologie peut dérouter les programmeurs habitués à d'autres langages.
Bien que le type soit character, R ne fait aucune distinction entre une lettre seule (appelée char dans plusieurs autres langages, mais pas en R) et une longue string.
Les deux sont un seul élément, et peuvent être entourés indifféremment de guillemets simples ' ' ou doubles " ".
Le guide de style recommande d'utiliser des guillemets doubles, sauf lorsque tu veux placer des guillemets doubles à l'intérieur de la string sans les échapper.
str2 <- 'String with "quotes"' # convenience format
str2
#> [1] "String with \"quotes\"" # standard format
Un character vector est donc un vecteur de strings, où chaque string est un seul élément.
s <- c("some", "strings")
s
#> [1] "some" "strings"
length(s)
#> [1] 2
length(s[1]) # the number of vector elements, not the string length
#> [1] 1
nchar(s[1]) # the number of characters in the string "some"
#> [1] 4
Dans l'exemple ci-dessus, length(s[1]) est équivalent à length("some") : le nombre d'éléments du vecteur, pas la longueur de la string.
Manipuler une string individuelle est évidemment possible, comme avec nchar() dans l'exemple ci-dessus, mais cela nécessite des fonctions particulières qui seront abordées plus bas.
Comme on l'a vu plus haut, certains caractères de ponctuation doivent être « échappés » en les faisant précéder d'une barre oblique inverse \.
Cette liste n'étonnera pas la plupart des programmeurs expérimentés, car ce sujet est plutôt bien normalisé d'un langage à l'autre.
Outre les guillemets (comme montré plus haut), elle comprend les sauts de ligne \n, les tabulations \t, la barre oblique inverse \\, et les symboles Unicode commençant par \U ou \u.
Pour éviter d'utiliser beaucoup de caractères d'échappement, il est possible de définir des strings brutes avec r"( )" :
r"(raw "string")"
#> [1] "raw \"string\""
Pour les strings brutes contenant des parenthèses ( ), il existe bien d'autres options de syntaxe.
Toute version de R actuellement prise en charge encode les strings en UTF-8, ce qui permet de prendre en charge la plupart des langues du monde.
"आधुनिक मानक हिन्दी"
#> [1] "आधुनिक मानक हिन्दी"
De plus, de nombreuses fonctions de R tiennent compte de la locale et essaient de suivre les conventions locales pour le tri des strings, la conversion en majuscules/minuscules, les dates et les heures, etc.
Ce programme montrera principalement des exemples en anglais américain : à la fois parce que c'est un choix par défaut courant, et parce que les auteurs du programme se trouvent être résidents des États-Unis.
Les locales sont un sujet complexe, donc n'hésite pas à chercher de la documentation adaptée à ta situation.
La plupart des choses sont possibles en R de base, mais beaucoup de programmeurs trouvent la prise en charge de la manipulation des strings plutôt limitée et déroutante.
Heureusement, il existe des paquets tidyverse bien plus intuitifs, flexibles et cohérents.
Il reste beaucoup de code hérité qui utilise les anciennes fonctions de manipulation de strings. C'est notamment le cas de la plupart des solutions de la communauté sur Exercism.
De plus, pour de petites tâches, il peut être utile d'utiliser des fonctions intégrées plutôt que d'importer une grande bibliothèque. Les mainteneurs du parcours R ont tendance à adopter cette approche lorsqu'ils écrivent les solutions exemplaires pour valider les exercices sur notre dépôt GitHub.
Par conséquent, il vaut encore la peine de connaître un peu les anciennes approches.
Les fonctions ci-dessous sont présentées dans leur forme la plus simple, mais la plupart ont aussi des arguments optionnels. Consulte la documentation pour plus de détails.
ncharRenvoie le nombre de caractères d'une string.
nchar("string")
#> [1] 6
nchar("Hrōðgār") # a person in Beowulf, written in Old English
#> [1] 7
Le sujet (complexe !) de ce que signifie « caractère » dépasse notre propos. Note simplement quelques points dans les exemples ci-dessus :
\0 (ce qui ajouterait 1 à la longueur).Fais attention à ce que tu passes à nchar.
Les résultats peuvent encore être imprévisibles pour les valeurs qui ne sont pas de type character, même si cela s'est amélioré dans les versions récentes de R.
grepRecherche un motif dans un vecteur de strings et renvoie les indices qui correspondent.
grep("Jav", c("Java", "Python", "Javascript"))
#> [1] 1 3
paste et paste0
Combine un nombre arbitraire de strings en une seule string.
Un séparateur peut éventuellement être spécifié pour paste : la valeur par défaut est un espace.
paste0(v) est équivalent à paste(v, sep = "").
paste("bits", "of", "a", "string")
#> [1] "bits of a string"
paste0("bits", "of", "a", "string")
#> [1] "bitsofastring"
sprintfPour un contrôle plus fin de l'assemblage des strings, R reprend la fonction sprintf du C (et de plusieurs langages plus récents : Wikipédia en recense environ 30).
r <- 5.3
pi <- 3.14159
sprintf("A circle of radius %.1f has area %.2f", r, pi * r^2)
#> [1] "A circle of radius 5.3 has area 88.25"
trimwsLes espaces en début et/ou en fin de chaîne peuvent être supprimés avec trimws() :
s <- " messy string "
trimws(s)
#> [1] "messy string"
Il existe un paramètre which = "left" (ou "right") pour ne supprimer les espaces que d'un seul côté.
substrOpère sur une seule string et renvoie une sous-chaîne comprise entre deux limites (incluses).
substr("Exercism", 5, 7)
#> [1] "cis"
strsplitDécoupe une string en une liste R, en se basant sur une string de séparation. Le séparateur est obligatoire (sans valeur par défaut), mais peut être une string vide.
strsplit("R, Python, Julia", ", ")
[[1]]
#> [1] "R" "Python" "Julia"
strsplit("Exercism", "")
[[1]]
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"
Les listes seront abordées dans un Concept distinct.
Si tu n'as pas encore atteint cette partie du programme, sache pour l'instant que la fonction unlist() convertira (dans ce cas) une liste en vecteur.
unlist(strsplit("Exercism", ""))
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"
regexpr et regexpr
Abréviation de « expression régulière », ces fonctions recherchent les occurrences d'un motif dans une string et renvoient une liste de données (potentiellement) utiles. Elles ne sont mentionnées que pour être exhaustif : il existe de meilleures options !
regexpr ne renvoie que la première occurrence, gregexpr les renvoie toutes (« g » pour une recherche « globale »).
Comme leurs noms l'indiquent, le motif peut être une expression régulière, qui sera abordée dans un Concept ultérieur.
Pourquoi ces anciennes fonctions de manipulation de strings ressemblent-elles à des commandes du shell Unix ?
Bien que R ait été créé en 1993, il a été conçu pour être largement rétrocompatible avec le langage S.
S a été conçu aux Bell Labs, où il est apparu pour la première fois en 1976. Les Bell Labs étaient aussi à l'origine d'Unix (à partir de 1969) et du langage C (1972).
Toute une équipe, issue d'une époque bien plus ancienne ! On ne peut que supposer qu'ils se parlaient et échangeaient des idées.
Pour être indulgent, on pourrait dire que les anciennes fonctions de manipulation de strings ne sont pas ce que R fait de mieux (des avis moins indulgents existent).
Pour améliorer la situation, la bibliothèque stringr est en développement depuis 2009 et fait aujourd'hui partie intégrante de la collection Tidyverse.
Importe-la dans ton espace de noms avec library(stringr) ou library(tidyverse).
Parmi ses fonctionnalités clés :
str_ et cherchent à rendre leur objectif clair.Comme tous les paquets du Tidyverse, stringr dispose d'une bonne documentation, généralement écrite par le ou les auteurs du paquet.
stringr propose des dizaines de fonctions, qui ont souvent une variété d'arguments optionnels.
Cela peut être un peu décourageant au début, alors la documentation est ton amie.
Réfléchis à ce que tu veux fournir en entrée (une seule string ? un vecteur ? quelque chose de plus complexe ?), et à ce que tu veux obtenir en sortie.
Il y a probablement un moyen simple de faire ce que tu veux, si tu trouves la bonne fonction et les bons arguments.
L'utilisation des pipes |> pour connecter des fonctions sera abordée plus en détail dans d'autres parties du programme.
Pour l'instant, il est utile de savoir que :
L'exemple ci-dessous illustre la syntaxe. Les fonctions individuelles seront abordées plus loin.
library(stringr)
"Monday-25-February" |>
str_split("-") |>
unlist() |> # [1] "Monday" "25" "February"
str_sub(1, 3) |> # [1] "Mon" "25" "Feb"
str_to_upper() |> # [1] "MON" "25" "FEB"
str_flatten(collapse = ", ")
#> [1] "MON, 25, FEB"
str_length compte les points de code (semblable à nchars).
Diverses autres fonctions manipulent les espaces, notamment str_trim() et str_squish().
C'est souvent une tâche de nettoyage essentielle au début de tout projet de science des données.
# The first 3 kernels in JuPyteR notebooks:
str_length(c("Julia", "Python", "R")) #> [1] 1 6 5
#> [1] 5 6 1
str_pad(c("Julia", "Python", "R"), 8, "right") # add spaces if necessary
#> [1] "Julia " "Python " "R "
str_trim("Julia Python R ") # remove leading & trailing whitespace
#> [1] "Julia Python R"
str_squish("Julia Python R ") # trim, collapse multiple spaces
#> [1] "Julia Python R"
La fonction str_sub(string, start, end) renvoie la sous-chaîne allant de start à end (inclus), tandis que str_sub_all() peut opérer sur plusieurs strings.
La fonction est très flexible.
start et end valent par défaut le premier et le dernier caractère, respectivement.
Ils peuvent être des vecteurs d'indices.
Contrairement à l'indexation des vecteurs, et c'est tant mieux, les valeurs négatives comptent à partir de la fin de la string, à la manière de Python.
s <- "abcdefgh"
str_sub(s, 2, 4)
#> [1] "bcd"
str_sub(s, 5)
#> [1] "efgh"
str_sub(s, -3, -2)
#> [1] "fg"
str_sub(s, c(1, 4), c(2, 5))
#> [1] "ab" "de"
str_sub(s, c(1, 4, 6), c(2, 5, -1))
#> [1] "ab" "de" "fgh"
s |> str_sub(2, 4) # these functions are designed for pipes
#> [1] "bcd"
# str_sub_all takes vector input, gives list output
str_sub_all(c(s, "ijklmnop"), c(1, 4, 6), c(2, 5, -1))
[[1]]
#> [1] "ab" "de" "fgh"
[[2]]
#> [1] "ij" "lm" "nop"
str_flatten convertit un vecteur de caractères en une seule string.
L'argument collapse est inséré entre chaque élément et vaut "" par défaut.
c("R", "Julia", "Python") |> str_flatten()
#> [1] "RJuliaPython"
c("R", "Julia", "Python") |> str_flatten(collapse = " - ")
#> [1] "R - Julia - Python"
str_c convertit plusieurs vecteurs de caractères en un seul vecteur de caractères, avec recyclage si nécessaire.
str_c(LETTERS[1:8], 1:8, sep = ":")
#> [1] "A:1" "B:2" "C:3" "D:4" "E:5" "F:6" "G:7" "H:8"
str_glue apporte à R une interpolation de strings simple.
Les expressions entre accolades { } sont interprétées et leur représentation textuelle est substituée.
Si plusieurs strings d'expressions sont fournies, elles seront aussi concaténées comme avec str_flatten.
Les strings peuvent aussi, éventuellement, être débarrassées des espaces en début et en fin.
r <- 5.3
pi <- 3.14159
str_glue("A circle of radius {r} has area {pi * r^2}")
A circle of radius 5.3 has area 88.2472631
Bien que str_glue soit flexible (et assez compliquée), la bibliothèque glue sous-jacente offre un éventail de possibilités encore plus large.
Un usage très courant de R consiste à transformer des données désordonnées en un format cohérent pour l'analyse. Pour cela, il existe plusieurs façons de découper un texte en sous-chaînes.
str_split_1 est la plus simple : elle convertit une seule string en un vecteur de morceaux, découpés selon un motif donné (il n'y a pas de motif par défaut).
s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R" "Julia" "Python"
str_split_i prend un vecteur de caractères et un indice n, et renvoie un vecteur contenant la n-ième sous-chaîne de chaque string d'entrée.
s2 <- c(s, "C Kotlin, F#") #> [1] "R Julia Python" "C Kotlin, F#"
str_split_i(s2, " ", 2) # 2nd element of each
#> [1] "Julia" "Kotlin,"
str_split prend un vecteur de caractères et renvoie une liste de vecteurs.
Notre programme comporte un Concept sur les listes, qui explique ce résultat.
str_split(s2, " ")
[[1]]
#> [1] "R" "Julia" "Python"
[[2]]
#> [1] "C" "Kotlin," "F#"
Il existe aussi str_split_fixed, qui renvoie une matrice.
Les matrices seront abordées dans un Concept ultérieur.
Pour comparer la string entière, str_equal() peut s'appuyer sur diverses définitions de l'« égalité ».
Le plus simplement, le comportement par défaut est sensible à la casse, mais cela peut être modifié.
Les locales et Unicode ouvrent bien d'autres possibilités.
str_equal("Odin", "odin")
#> [1] FALSE
str_equal("Odin", "odin", ignore_case = TRUE)
#> [1] TRUE
Il existe de nombreuses autres fonctions, qui seront abordées plus en détail dans le Concept sur les expressions régulières.
Certaines langues (principalement européennes) distinguent les lettres majuscules des minuscules.
Un ensemble de fonctions de stringr permet de passer de l'une à l'autre, en respectant les conventions de la locale :
str_to_lower()str_to_upper()str_to_title() : première lettre de chaque mot en majusculestr_to_sentence() : première lettre de la phrase en majusculestr_to_upper("elixir") # defaults to locale = "en"
#> [1] "ELIXIR"
str_to_upper("ελληνικά") # infers Greek
#> [1] "ΕΛΛΗΝΙΚΆ"
str_to_title("the cat sat on the mat")
#> [1] "The Cat Sat On The Mat"
str_to_sentence("the cat sat on the mat")
#> [1] "The cat sat on the mat"
Pour remplacer des sous-chaînes par correspondance de motif, str_replace() modifie la première occurrence du motif, str_replace_all() les modifie toutes.
Pour remplacer des sous-chaînes par indexation de caractères, il existe une forme d'affectation de str_sub().
s <- "abcde"
str_sub(s, 3, 4) <- "yz"
s
#> [1] "abyze"
str_dup crée des doublons et str_unique supprime les doublons.
Par défaut, str_dup n'a pas de séparateur, mais on peut en spécifier un.
str_dup("xyz", 4, sep = ", ")
#> [1] "xyz, xyz, xyz, xyz"
str_unique est une extension de unique().
Les deux suppriment les doublons d'un vecteur, mais la fonction de stringr peut aussi gérer la sensibilité à la casse et diverses correspondances propres à la locale.
inp <- c("some", "strings", "Some", "Strings")
str_unique(inp)
#> [1] "some" "strings" "Some" "Strings"
str_unique(inp, ignore_case = TRUE)
#> [1] "some" "strings"
La bibliothèque stringr repose sur la bibliothèque stringi de fonctions de plus bas niveau.
Si tu as des besoins particuliers qui n'ont pas déjà été abordés ci-dessus, alors :