Ruby et les encodages, une sacrée bonne idée !

Billet d'humeur

Ah, Ruby…

Ruby et sa merveilleuse idée d’avoir une unique classe pour représenter tous les types de chaînes (brutes comme encodées), de tous les encodages (UTF-8, UTF-16, latin-1, etc.), à savoir String.

Oui, en Ruby, deux instances de String peuvent avoir des encodages différents. Des types non compatibles entre-eux, mais instances d’une même classe, et seulement différenciés par leur attribut encoding.

1
2
3
4
5
6
7
8
irb> utf8_str = 'âbc'
=> "âbc"
irb> latin1_str = 'âbc'.encode('iso-8859-1')
=> "\xE2bc"
irb> utf8_str.encoding
=> #<Encoding:UTF-8>
irb> latin1_str.encoding
=> #<Encoding:ISO-8859-1>

Enfin, quand je dis non compatibles, ce serait encore assez logique, mais c’est plus fourbe que ça !
Parfois compatibles, parfois pas. Ruby choisit de ne planter que le plus tard possible, donc si les caractères des deux chaînes n’entrent pas en conflit, tout se passe bien.

Petit exemple avec la concaténation d’une chaîne UTF-8 avec une latin-1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
irb> 'abc' + 'abc'.encode('iso-8859-1') # Là c'est ok
=> "abcabc"
irb> 'âbc' + 'abc'.encode('iso-8859-1') # Là aussi
=> "âbcabc"
irb> 'abc' + 'âbc'.encode('iso-8859-1') # Toujours ok
=> "abc\xE2bc" # Ouais, dans un cas ça retourne une chaîne UTF-8, dans l'autre une latin-1, yolo
irb> 'âbc' + 'âbc'.encode('iso-8859-1') # Là ça casse tout
Encoding::CompatibilityError: incompatible character encodings: UTF-8 and ISO-8859-1
    from (irb):5
    from /usr/bin/irb:11:in `<main>'

Mais s’il n’y avait que ça…

On a aussi en Ruby la possibilité de créer des chaînes de caractères invalides. En effet, Ruby ne plantera pas lors de la création d’une chaîne invalide, mais seulement lors de l’exécution de certaines opérations (pas toutes, faut pas rigoler).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
irb> invalid_str = "\xE9"
=> "\xE9"
irb> invalid_str.valid_encoding? # Il sait qu'elle est invalide
=> false
irb> invalid_str + '!' # Mais ça n'a pas l'air de trop le gêner
=> "\xE9!"
irb> invalid_str.gsub('', '') # Ah ben si, un peu quand même
ArgumentError: invalid byte sequence in UTF-8
    from (irb):22:in `gsub'
    from (irb):22
    from /usr/bin/irb:11:in `<main>'

On croirait presque que le langage nous incite à créer des chaînes invalides, quand on voit que la classe String comporte une méthode force_encoding. Cette dernière permet tout simplement de redéfinir l’encodage d’une chaîne, sans pour autant la réencoder.

1
2
3
4
5
6
7
8
irb> latin1_str = 'ça pue'.encode('iso-8859-1')
=> "\xE7a pue"
irb> latin1_str.valid_encoding? # Elle est pour l'instant valide
=> true
irb> latin1_str.force_encoding('utf-8') # Et hop, UTF-8 !
=> "\xE7a pue"
irb> latin1_str.valid_encoding? # Tant pis :/
=> false

Je n’ai pas fait le tour de tous les langages existant, mais on ne doit pas être loin d’avoir là une des pires implémentations de l’Unicode et des multiples encodages.

Rendez-moi Python :'(



Alors, convaincus ?

2 commentaires

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte