Programmation Ruby/Types standards

Nous allons voir ici tous les types que nous pouvons considérer comme "standards", dans le sens où nous les retrouvons dans la plupart des langages, et que ceux-ci sont directement intégrés à l'interpréteur (built-in). Néanmoins il ne faut pas perdre de vue qu'il s'agit en réalité d'objets.

Pour rappel les méthodes dont le nom se termine par ! sont non pures : elles modifient l'objet.

Chaîne de caractères

modifier

En ruby les chaînes de caractères sont représentées par l'objet "String". En réalité cet objet contient et permet la manipulation d'un nombre indéfini d'octets, typiquement des caractères, nous pourrons néanmoins y stocker tout type de données binaires ou non.

Créer une chaîne de caractères

modifier

En ruby il existe une multitude de manières de créer une chaîne de caractères, par exemple en créant une instance de l'objet String :

maChaine = String.new("Une chaîne de caractères")

Mais le moyen le plus courant de créer une chaîne est de la placer entre simple quote (') ou entre double quote ("). Néanmoins ces deux moyens ne sont pas équivalents : en utilisant les doubles quotes ruby évaluera les expressions contenues entre #{ et }, ainsi que les caractères d'échappements.

i = 100

puts "la valeur de i est \n #{i}"
                                       => La valeur de i est
                                          100

puts 'la valeur de i est \n #{i}'
                                       => la valeur de i est \n #{i}

Dans une chaîne de caractère \ a une valeur particulière, elle permet de placer un caractères d'échappements. Le caractère \ indique à ruby que le caractère suivant possède une valeur particulière : ainsi "\n" représente une nouvelle ligne. Les caractères d'échappements ne sont pas pris en compte avec une chaîne de caractères débutant par un simple quote, néanmoins nous pouvons l'utiliser pour indiquer à Ruby de considérer le simple quote ou le backslash suivant comme faisant partie de la chaîne :

puts 'un simple quote : \' qui ne ferme pas la chaîne, et ici un backslash : \\'
                                       => un simple quote : ' qui ne ferme pas la chaîne, et ici un backslash : \

Un autre moyen de créer une chaîne est d'utiliser %q ou %Q, qui équivalent respectivement au simple et au double quote. Néanmoins l'utilisation de %q ou %Q permet de définir le caractère d'ouverture et de fermeture de la chaîne :

%q!j'utilise le caractère \! pour ouvrir ou fermer la chaîne, ' et " peuvent ainsi être utilisés sans \ !
%q*je peux utiliser n'importe quel caractère non alpha-numérique qui n'est pas dans la chaîne elle même*

Si nous avons plusieurs lignes de texte à écrire nous pouvons utiliser les caractères << suivit d'un identifiant pour créer une chaîne ayant les mêmes propriétés qu'une chaîne entre double quote, ou les caractères <<- suivis d'un identifiant entre simple quote pour créer une chaîne de caractères ayant les mêmes propriétés qu'une chaîne entre simple quote :

i = 100
maChaine <<DEBUT
   une chaîne de caractères
   i vaut #{i}
DEBUT
                                      => une chaîne de caractères
                                         i vaut 100

i = 100
maChaine <<-'DEBUT'
   une chaîne de caractères
   i vaut #{i}
DEBUT
                                      => une chaîne de caractères
                                         i vaut #{i}

Le dernier moyen d'obtenir une chaîne de caractères est d'utiliser la méthode to_s que la plupart des objets implémentent, ainsi :

42.to_s                                => "42"

Comparaisons de chaînes

modifier

Les chaînes de caractères peuvent être comparées comme indiqué dans le chapitre Expressions booléennes grâce aux méthodes <, <=, ==, >=, >

"abcdef" <=> "abcde"           =>  	 1
"abcdef" <=> "abcdef"          => 	 0
"abcdef" <=> "abcdefg"         => 	-1
"abcdef" <=> "ABCDEF"          =>       1    # Les caractères minuscules sont supérieurs aux caractères majuscules
"abc" <=> "acc"                =>      -1
  
"a" < "b"                      =>       true
"a" < "a"                      =>       false
"a" > "b"                      =>       false
"a" > "a"                      =>       false
"a" <= "b"                     =>       true
"a" <= "a"                     =>       true
"a" >= "b"                     =>       false
"a" >= "a"                     =>       true

"a" == "a"                     =>       true
"a" == "b"                     =>       false

"a" != "a"                     =>       false
"a" != "b"                     =>       true

La méthode est équivalente à <=>, à part que la casse n'est pas comparée :

"abcdef".casecmp("ABCDEF")     =>       0

Manipulation de chaînes

modifier

Taille d'une chaîne et index des caractères

modifier

length permet de connaître la taille d'une chaîne de caractères :

"Bonjour le monde".length => 16

index et rindex
modifier

index renvoie l'index de la première occurrence d'une sous chaîne dans la chaîne de caractères, renvoi nil si aucune occurrence n'a été trouvée :

"hello".index('e')                =>   1
"hello".index('lo')               =>   3
"hello".index('a')                =>   nil

Si un entier est donné en second paramètre, celui-ci indique l'index où commencer la recherche.

"Bonjour le monde".index('o',7)   =>   12

Nous pouvons également utiliser une expression rationnelle.

rindex effectue le même travail à la différence près que la recherche se fait à partir de la fin de la chaîne de caractères :

"Bonjour le monde".rindex('o')     =>   12

Si le second paramètre est présent, la recherche commencera au caractère indiqué :

"Bonjour le monde".rindex('o',9)   =>   4

À noter que si le second paramètre est négatif, alors l'index est compté à partir de la fin de la chaîne :

"Bonjour le monde".rindex('o',-9)  =>   4
"Bonjour le monde".rindex('o',-2)  =>   12

Opérateurs sur les chaînes de caractères

modifier

L'opérateur de multiplication renvoie une chaîne de caractères contenant n fois la chaîne représentée par l'objet :

"Ho! " * 3 => Ho! Ho! Ho!

L'opérateur d'addition permet de concaténer deux chaînes de caractères :

maChaine = "le Monde"
"Bonjour " + maChaine     => Bonjour le Monde
[] et []=
modifier

L'opérateur [] permet de récupérer un ou plusieurs caractères faisant partie d'une chaîne. Si un entier est passé en paramètre, l'opérateur renverra le code ASCII du caractère correspondant (le premier caractère est indexé par la valeur 0) :

maChaine = "Bonjour le monde"

maChaine[3]                => 106 # Correspond au code ASCII du 4e caractère (j)

Si deux entiers sont passés en paramètres, l'opérateur renverra une sous-chaîne commençant à l'index passé en premier paramètre et de longueur indiquée par le second élément :

maChaine[1,5]              =>  onjou

On peut également indiquer un intervalle :

maChaine[1..6]             =>  onjou

Il est à noter que si l'index est négatif, ruby compte à partir de la fin de la chaîne :

maChaine[-3,5]             => nde

On peut également utiliser une expression rationnelle, nous verrons cela plus en détail dans le chapitre correspondant.

L'opérateur []= permet de changer le contenu d'une chaîne de caractères. L'utilisation est semblable à celle de [], à la différence que plutôt que de renvoyer un ensemble de caractères, l'opérateur modifiera la chaîne :

maChaine[3] = "V"
puts maChaine              => BonVour le monde
maChaine[8] = "tout "
puts maChaine              => Bonjour tout le monde

Si deux entiers sont passés en paramètres, ruby remplacera la chaîne commençant à l'index passé en première paramètre et d'une longueur passée en second paramètre :

maChaine[8,8] = "Roger"    
puts maChaine              => Bonjour Roger        

maChaine[8..16] = "Roger"
puts maChaine              => Bonjour Roger

On peut directement utiliser une chaîne de caractères ou une expression rationnelle :

maChaine["Bonjour"] = "Hello"
puts maChaine              => Hello le monde


Formatage de chaîne

modifier

split permet de séparer les différents éléments d'une chaîne en fonction d'un ou plusieurs délimiteurs, par défaut ceux-ci sont les fins de ligne et les espaces (\s, \t, \r,\n et \r\n). La méthode renvoi un tableau contenant les différents éléments :

"Bonjour le monde".split   =>      ["Bonjour", "le", "monde"]
"B.o.n.j.o.u.r".split('.') =>      ["B","o","n","j","o","u","r"]

Si un entier est donné en paramètre, le tableau résultant n'aura comme nombre d'éléments la valeur de cet entier :

 "B.o.n.j.o.u.r".split('.',3) =>      ["B","o","n.j.o.u.r"]

Nous pouvons également utiliser une expression rationnelle.

chomp, chomp!, chop et chop!
modifier

chop et chop! permettent de supprimer le dernier caractère d'une chaîne, néanmoins si les deux derniers caractères sont "\r\n", les deux caractères sont supprimés :

"Bonjour\r\n".chop         =>      "Bonjour"
"Bonjour\n\r".chop         =>      "Bonjour\n"
"Bonjour\n".chop           =>      "Bonjour"
"Bonjour".chop             =>      "Bonjou"

chomp et chomp! suppriment le dernier caractère seulement si celui-ci est un caractère de fin de ligne (soit "\r", "\n" et "\r\n") :

"Bonjour".chomp             =>      "Bonjour"
"Bonjour\n".chomp           =>      "Bonjour"
"Bonjour \n le monde".chomp =>      "Bonjour \n le monde"

Si une chaîne de caractères est donnée en paramètre, celle-ci est supprimée si elle est termine la chaîne :

"Bonjour".chomp("jour")     =>      "Bon"
"Bonjour\r\n".chomp("jour") =>      "Bonjour\r\n"

Pour rappel, les méthodes finissant par "!" sont des méthodes non pures : elles modifient l'objet appelant.

downcase, downcase!, upcase, upcase!, swapcase, swapcase!, capitalize et capitalize!
modifier

downcase et upcase permettent de passer respectivement tous les caractères en majuscules ou en minuscules.

"BonjOUr".downcase          =>       "bonjour"
"BonjOUr".upcase            =>       "BONJOUR"

swapcase inverse la casse :

"BonjOUr".swapcase          =>       "bONJouR"

capitalize ne met que le premier caractère en majuscule :

"bonjOUr".capitalize        =>       "Bonjour"

L'opérateur % permet de formater la chaîne de caractères. Le format utilisé est le même que celui de la fonction sprintf de ruby et de bien d'autres langages tel le C. Le format est décrit par le caractère % suivi d'un indicateur optionnel, un indicateur de taille, de précision et de type.

Les indicateurs de type :

Indicateur Description
b données binaire
c caractère
d ou i nombre entier
e convertit un nombre entier sous sa forme exponentielle avec un chiffre avant la virgule, la précision indique le nombre de chiffres après la virgule (par défaut 6)
E comme e mais utilise le caractère majuscule E pour indiquer l'exposant
f nombre flottant, la précision indique le nombre de chiffres après la virgule
g comme e mais converti en nombre flottant
G comme E mais converti en nombre flottant
o nombre octal
p symbole
s chaîne de caractères, si une précision est donnée, alors elle indique le nombre de caractères
u nombre entier non signé (pas de signe)
x hexadécimal en utilisant les caractères minuscules (par exemple f)
X hexadécimal en utilisant les caractères majuscules (par exemple F)

Format :

Indicateur Types applicable Description
espace bdeEfgGioxXu Laisse un espace au début des nombres positifs
# bdeEfgGioxXu Format alternatif, pour les types o, x, X et b préfixe respectivement le résultat par 0, 0x, 0X, et 0b
+ bdeEfgGioxXu Ajoute un + au début des nombres positifs
- bdeEfgGioxXu Ajoute un - au début des nombres négatifs
0 tous Remplit le format avec des 0 plutôt que des espaces
. tous Prend l'argument suivant comme taille pour aligner à droite s'il est positif. Si l'argument est négatif, aligne à gauche
"%.3s" % 42424242      =>      "424"

Si plusieurs éléments doivent être formatés, il faut passer les paramètres dans un tableau :

"%d %04x" % [12.5, 42] =>      "12 002a"

La méthode unpack permet de décoder des chaînes de caractères (ou contenant des données binaires) en corrélation avec une chaîne de format.

Indicateur Description Type renvoyé
A Chaîne de caractères en supprimant les caractères vides (espace, tabulation...) String
a Chaîne de caractères String
B Extrait les bits de chaque caractère (bit de poids fort en premier) String
b Extrait les bits de chaque caractère (bit de poids faible en premier) String
C Extrait un caractère comme un entier non signé Fixnum
c Extrait un caractère comme un entier signé Fixnum
d Considère sizeof(double) caractères comme un double Float
E Considère sizeof(double) caractères comme un double en little-endian Float
e Considère sizeof(float) caractères comme un flottant en little-endian Float
f Considère sizeof(float) caractères comme un flottant Float
G Considère sizeof(double) caractères comme un double dans l'ordre réseau Float
g Considère sizeof(float) caractères comme un flottant dans l'ordre réseau Float
H Extrait le code hexadécimal de chaque caractère (bit de poids fort en premier) String
h Extrait le code hexadécimal de chaque caractère (bit de poids faible en premier) String
I Considère sizeof(int) caractères comme un entier non signé Integer
i Considère sizeof(int) caractères comme un entier signé Integer
L Considère 4 caractères consécutifs comme un entier long non signé Integer
i Considère 4 caractères consécutifs comme un entier long signé Integer
M Décode les chaînes "quoted printable" String
m Décode les chaînes en Base64 String
N Considère 4 caractères consécutifs comme un entier long non signé dans l'ordre réseau Fixnum
n Considère 2 caractères consécutifs comme un entier court non signé dans l'ordre du réseau Fixnum
P Considère sizeof(char *) comme un pointeur et renvoi la taille de la chaîne ainsi référencée String
P Considère sizeof(char *) comme un pointeur sur une chaîne terminée par le caractère null (\0) String
S Considère 2 caractères consécutifs comme un entier court non signé dans l'ordre natif du système Fixnum
s Considère 2 caractères consécutifs comme un entier court signé dans l'ordre natif du système Fixnum
U Extrait une chaîne encodée au format UTF8 comme des entiers non signés Integer
u Extrait une chaîne encodée en UU String
V Considère 4 caractères consécutifs comme un entier long non signé en "little endian" Fixnum
v Considère 2 caractères consécutifs comme un entier court non signé en "little endian" Fixnum
X Retourne en arrière d'un caractère N/A
x Avance d'un caractère N/A
Z Supprime les caractères null de fin String
@ Se déplace du nombre donné en argument N/A

La chaîne de format se compose d'un nombre de directives à piocher dans le tableau précédent, facultativement suivi d'un nombre indiquant le nombre de fois qu'il faut répéter cette directive, un astérisque (*) correspondant a tous les éléments restant. Les directives s,S,i,I,l et L peuvent être suivis d'un underscore (_) indiquant de choisir le format natif du système.

Exemples :

"abc \0\0abc \0\0".unpack('A6Z6')      => ["abc", "abc "]
"abc \0\0".unpack('a3a3')                     => ["abc", " \000\000"]
"aa".unpack('b8B8')                              => ["10000110", "01100001"]
"aaa".unpack('h2H2c')                          => ["16", "61", 97] 
"\xfe\xff\xfe\xff".unpack('sS')                 => [-2, 65534] 
"now=20is".unpack('M*')                      => ["now is"] 
"whole".unpack('xax2aX2aX1aX2a') => ["h", "e", "l", "l", "o"]

Itérateurs

modifier

Les itérateurs sont un mécanisme puissant de ruby, ils permettent de parcourir les éléments d'un objet. Nous verrons plus tard comment étendre facilement sa propre classe avec des itérateurs. Pour chaque type que nous allons voir, nous allons voir ses itérateurs. À l'heure actuelle considérons simplement un itérateur comme une méthode prenant un bloc pour paramètre (et d'ailleurs c'est ce que les itérateurs sont : de simples méthodes).

succ, succ! et upto

modifier

succ se contente de renvoyer l'élément succédant à la chaîne, en pratique succ incrémente le dernier caractère alphanumérique de la chaîne, si celui-ci a atteint sa limite, succ incrémentera l'avant-dernier caractère et ainsi de suite:

"abcd".succ                     => "abce"
"THX1138".succ                  => "THX1139"
"<<koala>>".succ                => "<<koalb>>"
"1999zzz".succ                  => "2000aaa"

upto itère à travers les valeurs successives d'une chaîne, jusqu'à arriver à la chaîne passée en paramètre. La méthode incrémente à partir de la dernière valeur alphanumérique de la chaîne :

"<<aa>>".upto("<<bb>>") do
  |i| puts i
end
                                => <<aa>>
                                   <<ab>>
                                   <<ac>>
                                   <<ad>>
                                   <<ae>>
                                   <<af>>
                                   <<ag>>
                                   <<ah>>
                                   <<ai>>
                                   <<aj>>
                                   <<ak>>
                                   <<al>>
                                   <<am>>
                                   <<an>>
                                   <<ao>>
                                   <<ap>>
                                   <<aq>>
                                   <<ar>>
                                   <<as>>
                                   <<at>>
                                   <<au>>
                                   <<av>>
                                   <<aw>>
                                   <<ax>>
                                   <<ay>>
                                   <<az>>
                                   <<ba>>
                                   <<bb>>

each_byte

modifier

each_byte permet d'itérer à travers la chaîne de caractères, octet par octet. La valeur qui sera envoyée au bloc sera le code ASCII (donc un entier) du caractère :

"bonjour".each_byte {|i| print i.to_s+" "} => 98 111 110 106 111 117 114

each permet d'itérer à travers chaque ligne contenue dans une String :

"H\nA\r\nL\n".each do
   |i| print i.succ
end                               => IBM

Il est à noter que l'itérateur each est celui appelé par la construction for..in' :

for i in "H\nA\r\nL\r"
  print i.succ
end
                                  => IBM

Valeur numérique

modifier

En ruby les valeurs numériques sont soit flottantes soit entières et sont de taille infinie (jusqu'à la limite de mémoire du système). Plus tôt dans le livre, nous avons indiqué qu'un type entier est de type Integer. En fait nous avons menti. Le type Integer permet en réalité de cacher le type réel de la valeur. En effet, les valeurs inférieures aux valeurs d'un entier sur le système (donc en général 32 ou 64 bits) sont en réalité de type Fixnum. Au delà, elles seront de types Bignum. En pratique la conversion est transparente pour le développeur :

i = 10
5.times do
    print i.class,"\n"
    i = i*i
end
                                                  =>   Fixnum
                                                          Fixnum
                                                          Fixnum
                                                          Fixnum
                                                          Bignum

Syntaxe

modifier

En ruby une valeur numérique peut s'écrire de différente forme, notamment en fonction de la base utilisée.

Classiquement, une valeur numérique peut s'écrire comme une suite de chiffres séparés éventuellement par des caractères de soulignement ( _) qui seront ignorés lors de l'interprétation. Une valeur négative est simplement préfixée par le signe moins (-) :

42                                          => 42
4_2                                        => 42
-42                                         => -42

On peut également travailler dans une autre base en préfixant la valeur numérique. Ainsi en préfixant avec 0 (zéro) on indique l'utilisation d'un nombre en base octale, 0x pour un nombre en hexadécimal et 0b pour un nombre en binaire :

0767                                     => 503
0xaabb                                 => 43707
0b01101101                        => 109

Utilisation

modifier

Un objet correspondant à une valeur numérique s'instancie simplement en écrivant cette valeur :

4                                           => 4
4.class                                 => Fixnum

Certains objets, comme l'objet String, possèdent une méthode to_i qui renvoie si possible une valeur entière, et to_f qui renvoi si possible un flottant :

"  58_87".to_i                     => 5887

Si la chaîne contient des éléments non numériques, à l'exception du caractère de soulignement ou du signe moins, la valeur renvoyée sera celle de la première valeur numérique trouvée et précédant les autres caractères :

"toto".to_i                            => 0
"to87to".to_i                        => 0
"87to".to_i                           => 87

"Opérateurs" arithmétiques

modifier

Attention : En Ruby les opérateurs n'existent pas, ce sont des méthodes inclusent dans l'objet Ruby père Object. On parle donc ici d'"opérateur" par analogie avec les langages orientés objet et procéduraux.

il faut bien comprendre que :

2 + 4 

équivaut à

2.+(4)


tel que

objet1 = 2
objet2 = 4
objet1.+(objet2) => 6

Pour simplifier la lecture, on accordera l'usage de cet abus de langage dans le reste du document pour faciliter le passage d'un autre langage vers Ruby


L'opérateur + permet d'additionner 2 valeurs numériques :

2+2                                    => 4
"3+4".to_i                         => 3

L'opérateur - permet de soustraire une valeur numérique à une autre :

42-10                                => 32
2-10                                   => -8

L'opérateur * permet de multiplier 2 valeurs numériques :

42*2                                 => 84

L'opérateur / permet de diviser une valeur numérique par une autre :

9/3                                   => 3

ATTENTION: Le type renvoyé est du type des opérandes, ainsi si la division n'est pas entière, seul le dividende est renvoyé:

9/4                                   => 2

par contre, si nous utilisons au moins un flottant, le type renvoyé sera de type flottant :

9.0/4                            => 2.25

Une division par 0 lève une exception (nous verrons les exceptions plus tard, il suffit de considérer à l'heure actuelle qu'il s'agit d'une erreur) et interrompt le cours du programme si celle-ci n'est pas interceptée :

9/0                                  => ZeroDivisionError: divided by 0

L'opérateur ** permet d'augmenter une valeur numérique à la puissance indiquée en paramètre :

9**2                               => 81
9**0                               => 1

L'opérateur % (modulo) permet de connaître le reste d'une division :

100%30                      => 10

Itérateurs

modifier

La classe Integer propose également certains itérateurs, comme pour tout autre itérateur, ceux-ci prennent un ou plusieurs paramètres ainsi qu'un bloc de code.

succ permet de récupérer la valeur numérique suivante :

8.succ                                                  => 9

times permet de créer une boucle allant de 0 à la valeur de l'objet :

5.times do |val|
   print val.to_s+'..'
end
                                                              => 0..1..2..3..4..

upto et downto

modifier

upto et downto permettent respectivement d'itérer à partir de la valeur de l'objet jusqu'à la valeur passée en paramètre, respectivement en incrémentant ou en décrémentant :

5.upto(8) do |i| print i.to_s+'..' end
                                                              => 5..6..7..8..

8.downto(5) do |i| print i.to_s+'..' end
                                                              => 8..7..6..5..

5.downto(8) do |i| print i.to_s+'..' end
                                                              => 5

step ressemble à upto et downto à part que l'on peut préciser le pas :

5.step(48, 5) do |i| print i.to_s+'..' end  #ici le second paramètre correspond au pas
                                                               => 5..10..15..20..25..30..35..40..45..

Expression rationnelle

modifier

Les expressions rationnelles (parfois nommées à tort expressions régulières) sont un mécanisme puissant mais qui peut être complexe. Elles permettent des recherches dans une chaîne de caractères selon des critères ou un modèle de recherche précis. Ceci permet la sélection d'une sous chaîne de caractères, ou la manipulation des chaînes ainsi trouvées (typiquement une substitution).

Ruby propose une classe pour l'utilisation des expressions rationnelles, les développeurs Perl seront heureux car son utilisation en Ruby est quasi-identique (voir Catégorie:Expressions rationnelles).