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

Chaînes de caractères en Julia

7 exercices

À propos de Chaînes de caractères

Une String en Julia est une séquence ordonnée de caractères. Comme le souligne le manuel : «\u00A0Bien sûr, la vraie difficulté surgit quand on se demande ce qu'est un caractère.\u00A0»

Une grande partie de cette complexité sera traitée plus en détail dans le concept Chars, mais en bref :

  • Toutes les strings en Julia sont Unicode.
  • Les strings qui se trouvent aussi être de l'ASCII (les lettres anglaises de A à Z en majuscules et en minuscules, les chiffres et quelques signes de ponctuation) sont faciles à utiliser.
  • La plupart des autres systèmes d'écriture du monde sont possibles à utiliser, mais demandent plus d'attention.

Littéraux de string

En général, les strings sont encadrées par des guillemets doubles " ". Les guillemets simples ' ' ne servent que pour les Chars.

Il est aussi possible d'utiliser des triples guillemets doubles """ """, ce qui permet d'employer " à l'intérieur de la string sans échappement. Plus important encore, l'indentation superflue est supprimée des strings multilignes.

julia> """
       Multiline
       String
       """
"Multiline\nString\n"

Échappement de caractères

Comme la plupart des langages depuis le C, Julia utilise une barre oblique inversée \ pour l'échappement :

  1. Pour saisir des caractères non imprimables, comme le saut de ligne \n de l'exemple précédent.
  2. Pour protéger des caractères qui auraient sinon une signification particulière, comme \$ (voir plus bas).

Itération

Une string est une collection itérable, donc une boucle for ... in fonctionne sans qu'il soit besoin de la convertir en vecteur de caractères.

julia> s = "Julia"
"Julia"

# admittedly, this is a silly example
julia> for c in s; print(c, "; "); end
J; u; l; i; a; 

Itérer de cette façon a l'avantage que Julia gère correctement les limites de caractères, même pour les strings non ASCII. L'importance de ce point apparaîtra plus clairement dans la section suivante.

Accès par indice

En principe, on peut accéder aux strings par leur indice exactement comme aux vecteurs :

julia> s[1], s[5]
('J', 'a')

Cela fonctionne manifestement pour une string ASCII comme «\u00A0Julia\u00A0», mais cela revient à ignorer la plupart des langues du monde.

Prenons l'un des caractères de Beowulf, écrit en vieil anglais il y a environ 15 siècles.

«\u00A0Hrōðgār\u00A0» compte de toute évidence 7 caractères, mais tous ne sont pas ASCII.

julia> king = "Hrōðgār"
"Hrōðgār"

julia> length(king)
7

julia> isascii(king)
false

# collect() converts to a Char vector:
julia> collect(king)
7-element Vector{Char}:
 'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
 'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)
 'ō': Unicode U+014D (category Ll: Letter, lowercase)
 'ð': Unicode U+00F0 (category Ll: Letter, lowercase)
 'g': ASCII/Unicode U+0067 (category Ll: Letter, lowercase)
 'ā': Unicode U+0101 (category Ll: Letter, lowercase)
 'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)

L'accès par indice sur une string comme celle-ci commence bien, mais finit par se casser à un moment donné :

julia> king[3]
'ō': Unicode U+014D (category Ll: Letter, lowercase)

julia> king[4]
ERROR: StringIndexError: invalid index [4], valid nearby indices [3]=>'ō', [5]=>'ð'

Le problème est que cet accès par indice compte des octets, qui, pour les strings non ASCII, n'ont pas de relation 1:1 avec les caractères. Un caractère Unicode/UTF-8 peut nécessiter jusqu'à 4 octets pour être représenté, et le concept Chars approfondira ce point.

Construction de strings

Concaténation

Beaucoup de langages utilisent + comme opérateur de concaténation de strings, mais pas Julia. Au moins, le message d'erreur est utile.

julia> "s1 " + "s2"
ERROR: MethodError: no method matching +(::String, ::String)
The function `+` exists, but no method is defined for this combination of argument types.
String concatenation is performed with * 

julia> "s1 " * "s2"
"s1 s2"

La fonction string() concatène un nombre arbitraire de strings.

julia> string("lots ", "of ", "bits")
"lots of bits"

La fonction join() fait la même chose, et accepte en plus un vecteur de strings qu'elle concatène avec un séparateur donné ("" par défaut).

julia> join(["lots", "of", "bits"], "_")
"lots_of_bits"

À noter que les strings sont immuables, et que la concaténation implique une copie. Ainsi, des concaténations répétées dans une boucle sont inefficaces, et il vaut mieux rassembler les fragments dans un vecteur et les joindre tous à la fin.

Répétition

Tout simple :

julia> repeat("Abc", 5)
"AbcAbcAbcAbcAbc"

Interpolation

Beaucoup de langages permettent d'insérer des valeurs calculées dans des strings, même si les avis divergent énormément sur la meilleure syntaxe à utiliser.

En Julia, les valeurs interpolées sont précédées de $.

julia> myvar = 42
42

julia> "myvar evaluates to $myvar"
"myvar evaluates to 42"

julia> r = 5.3
5.3

julia> "A circle of radius $r has area $(π * r^2)"
"A circle of radius 5.3 has area 88.24733763933729"

Les parenthèses sont obligatoires lorsque les omettre créerait une ambiguïté, mais facultatives pour un identifiant unique suivi d'un espace.

Pour inclure un $ dans la string, échappe-le avec \$.

Note la limite suivante : l'interpolation ne permet pas, pour l'instant, de formatage, comme le nombre de décimales à afficher.

Parmi les solutions de contournement :

  • Envelopper le calcul dans une fonction round().
  • En dehors de l'exécuteur de tests d'Exercism, le paquet PyFormattedStrings émule les f-strings de Python.
  • La macro @sprintf() est décrite ci-dessous.

@sprintf()

Pour un contrôle plus fin de l'assemblage des strings, Julia reprend la fonction sprintf du C (et de plusieurs langages plus récents : Wikipédia en recense environ 30).

En Julia, elle est implémentée sous forme de macro dans le module Printf, d'où le préfixe @.

julia> using Printf

julia> r = 5.3
5.3

julia> @sprintf("A circle of radius %.1f has area %.2f", r, π * r^2)
"A circle of radius 5.3 has area 88.25"

Fonctions sur les strings

Par souci de simplicité, cette section se concentrera sur les fonctions qui renvoient une nouvelle string et laissent l'entrée inchangée. Beaucoup ont un équivalent, dont le nom se termine par un !, qui modifie l'entrée sur place.

Longueur

Quelle est la longueur d'une string ? Parfois, cela dépend de la façon de compter.

Le cas simple est celui des strings ASCII, où chaque caractère occupe 1 octet.

julia> string_asc = "Julia"
"Julia"

julia> length(string_asc)
5

julia> sizeof(string_asc)
5

La fonction length() renvoie le nombre de caractères, et la fonction sizeof() le nombre d'octets.

Cette distinction devient importante avec d'autres jeux de caractères :

julia> king = "Hrōðgār"
"Hrōðgār"

julia> length(king)
7

julia> sizeof(king)
10

Découpage

Une opération courante en programmation consiste à prendre une string longue et à la découper en morceaux, selon une string séparatrice d'un ou plusieurs caractères.

Le plus généralement, on utilise la fonction split(). Le séparateur est par défaut (n'importe quel) espace, mais on peut en préciser d'autres.

julia> split("my little string")
3-element Vector{SubString{String}}:
 "my"
 "little"
 "string"

julia> split("My_snake_case_string", "_")
4-element Vector{SubString{String}}:
 "My"
 "snake"
 "case"
 "string"

Ces exemples découpent une string en strings plus petites. Découper une string en caractères est une opération différente.

julia> collect("input string")
12-element Vector{Char}:
 'i': ASCII/Unicode U+0069 (category Ll: Letter, lowercase)
 'n': ASCII/Unicode U+006E (category Ll: Letter, lowercase)
 'p': ASCII/Unicode U+0070 (category Ll: Letter, lowercase)
 'u': ASCII/Unicode U+0075 (category Ll: Letter, lowercase)
 't': ASCII/Unicode U+0074 (category Ll: Letter, lowercase)
 ' ': ASCII/Unicode U+0020 (category Zs: Separator, space)
 's': ASCII/Unicode U+0073 (category Ll: Letter, lowercase)
 't': ASCII/Unicode U+0074 (category Ll: Letter, lowercase)
 'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)
 'i': ASCII/Unicode U+0069 (category Ll: Letter, lowercase)
 'n': ASCII/Unicode U+006E (category Ll: Letter, lowercase)
 'g': ASCII/Unicode U+0067 (category Ll: Letter, lowercase)

Recherche et remplacement

Une string contient-elle une sous-chaîne donnée ? Curieusement, deux fonctions permettent de le tester : occursin() et contains() ne diffèrent que par l'ordre de leurs arguments.

julia> needle = "Julia"
"Julia"

julia> haystack = "Python, Julia and R"
"Python, Julia and R"

julia> occursin(needle, haystack)
true

julia> contains(haystack, needle)
true

Pour être plus précis sur la position :

julia> startswith(haystack, "Python")
true

julia> endswith(haystack, "Python")
false

Pour obtenir les indices début:fin d'une sous-chaîne, on dispose de plusieurs fonctions :

julia> findfirst(needle, haystack)
9:13

Pour remplacer des sous-chaînes, liste les changements avec la syntaxe x => y.

julia> replace("abc", "a"=>"b", "b"=>"c", "c"=>"a")
"bca"

Depuis Julia 1.7, la fonction replace peut prendre un nombre arbitraire de changements, avec la garantie qu'aucun caractère ne sera modifié plus d'une fois pendant le remplacement.

La syntaxe x => y s'appelle une pair, un type important en Julia qui est traité plus en détail dans le concept Dicts and Pairs.

Note : pour simplifier, tous les exemples ci-dessus utilisent des littéraux de string. Beaucoup de ces fonctions sont plus générales et fonctionnent aussi avec les expressions régulières. On y reviendra dans un prochain concept.

Espaces

Il est souvent nécessaire de supprimer les espaces au début et/ou à la fin d'une string.

Les fonctions pour cela sont lstrip() (à gauche), rstrip() (à droite) et strip() (les deux).

julia> strip("\n\t  useful_bit\n\n")
"useful_bit"

Modification de la casse

Ces fonctions ont des noms assez explicites :

julia> s = repeat("aBcD ", 5)
"aBcD aBcD aBcD aBcD aBcD "

julia> uppercase(s)
"ABCD ABCD ABCD ABCD ABCD "

julia> lowercase(s)
"abcd abcd abcd abcd abcd "

julia> titlecase(s)  # Initial letter of each word uppercase
"Abcd Abcd Abcd Abcd Abcd "

Conversion

Convertir des nombres en leur représentation sous forme de string, et inversement, est souvent utile.

La conversion d'un nombre en string est simple, même si une base autre que 10 par défaut nécessite un paramètre supplémentaire :

julia> string(42)
"42"

julia> string(42, base=16)
"2a"

julia> string(42, base=2)
"101010"

Convertir une string en valeur numérique est un peu plus compliqué, avec plus de risques d'erreur. Préciser le type numérique cible est obligatoire. Préciser la base est facultatif et vaut 10 par défaut.

julia> parse(Int, "42")
42

julia> parse(Float64, "42")
42.0

julia> parse(Int, "42?")
ERROR: ArgumentError: invalid base 10 digit '?' in "42?"

julia> parse(Int, "42 ")  # whitespace may be tolerated
42

Strings spéciales

Il est parfois utile de désactiver l'interpolation et l'échappement habituels à l'intérieur des strings. Pour cela, fais précéder le guillemet ouvrant de raw.

julia> x = 10
10

julia> "x = $x if we use \$ for interpolation"
"x = 10 if we use \$ for interpolation"

julia> raw"x = $x if we use \$ for interpolation"
"x = \$x if we use \\\$ for interpolation"

Les expressions régulières ont leur propre syntaxe compliquée, et Julia fournit deux types de strings pratiques : r" " pour les expressions régulières et s" " pour travailler avec les fragments capturés.

Diverses autres strings spéciales sont moins courantes dans la programmation de style Exercism, bien qu'elles soient très utilisées pour créer des bibliothèques.

  • Les strings de version sémantique v"x.y.z" gèrent la numérotation majeur.mineur.correctif des versions logicielles.
  • Les littéraux de tableau d'octets b" " contournent les limites de caractères Unicode et opèrent au niveau des octets.
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 2 exercices de plus pour t'entraîner sur Chaînes de caractères