Parcours
/
R
R
/
Programme
/
Chaînes de caractères
Ch

Chaînes de caractères en R

2 exercices

À propos de Chaînes de caractères

Contexte

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.

Échappement

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.

Locales

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.

Fonctions de manipulation de strings

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.

R de base

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.

nchar

Renvoie 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 :

  • Bien qu'implémentées en C en interne, les strings en R ne sont pas terminées par \0 (ce qui ajouterait 1 à la longueur).
  • Les caractères Unicode multi-octets sont gérés comme tu peux t'y attendre ou l'espérer. « Hrōðgār » compte 7 caractères (aussi appelés « runes » ou « points de code »), mais nécessite 10 octets de stockage.

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.

grep

Recherche 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"
sprintf

Pour 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"
trimws

Les 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é.

substr

Opère sur une seule string et renvoie une sous-chaîne comprise entre deux limites (incluses).

substr("Exercism", 5, 7)
#> [1] "cis"
strsplit

Dé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.

Advanced

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.

Tidyverse

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 :

  • Tous les noms de fonctions ont un préfixe str_ et cherchent à rendre leur objectif clair.
  • L'ordre des arguments cherche à être cohérent, avec les données d'entrée en premier argument.
  • Les entrées peuvent aller d'une string unique à des structures de données complexes. Les fonctions s'adaptent et font au mieux.

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.

Digression : une introduction rapide aux pipes

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 :

  • Un résultat à gauche du pipe devient le premier argument de la fonction à sa droite (où « droite » ignore les sauts de ligne facultatifs).
  • Les autres arguments peuvent être inclus dans l'appel de fonction.
  • En R, on doit diriger le pipe vers un appel de fonction, pas seulement vers un nom, donc les parenthèses sont toujours nécessaires (contrairement à certains autres langages).

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"
Gérer les longueurs

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"
Extraire des sous-chaînes

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"
Concaténer des strings

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.

Découper des strings

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.

Comparer des strings

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.

Modifier des strings

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 majuscule
  • str_to_sentence() : première lettre de la phrase en majuscule
str_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"
Divers

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"
Advanced

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 :

  1. Consulte la documentation de stringr, qui couvre plus d'options que nous ne pouvons en mentionner, même dans ce long document de Concept.
  2. Consulte la documentation de stringi, qui décrit bien d'autres fonctionnalités.
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet

Apprends Chaînes de caractères

L'entraînement est verrouillé

Déverrouille 1 exercice de plus pour t'entraîner sur Chaînes de caractères