programmez-en-d/caracteres.whata

366 lines
24 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[set
title = "Caractères"
partAs = chapitre
translator = "Raphaël Jakse"
proofreader = "Stéphane Goujet"
]
Les caractères sont des briques élémentaires des chaînes. Chaque symbole d'un système d'écriture est appelé caractère~ : les lettres des alphabets, les chiffres, les signes de ponctuation, l'espace, etc. Les briques élémentaires des caractères elles-mêmes sont appelées caractères également, ce qui entraîne certaines ambiguïtés.
Les tableaux de caractères constituent les chaînes. Nous avons vu les tableaux dans le chapitre précédent~ ; les chaînes de caractères seront vues dans deux chapitres.
Comme n'importe quelle autre donnée, les caractères sont aussi représentés par des valeurs entières composées de bits. Par exemple, la valeur entière d'un 'a' minuscule est 97 et la valeur entière du chiffre '1' est 49. Ces valeurs ont été choisies principalement par convention quand la table ASCII a été écrite.
Dans la plupart des langages, les caractères sont représentés par le type [c char], qui ne peut stocker que 256 valeurs distinctes. Si vous êtes déjà familier-ère avec le type [c char] dans d'autres langages, vous savez déjà probablement que ce n'est pas assez pour suppporter les glyphes de beaucoup de systèmes d'écritures. Avant de parler des trois types de caractères du D, faisons un peu d'histoire sur les caractères dans les systèmes d'information.
[ = Histoire
[ = Table ASCII
La table ASCII a été écrite à une époque où le matériel informatique était très limité comparé aux systèmes modernes. Basée sur 7 bits, la table ASCII peut représenter 128 codes différents. C'est suffisant pour représenter des caractères comme les versions minuscules et majuscules des 26 lettres de l'alphabet latin, les chiffres, les signes de ponctuations couramment utilisés, et quelques caractères de contrôle pour le terminal.
Par exemple, les codes ASCII des caractères de la chaîne [c "hello"] sont les suivants : ``104``, ``101``, ``108``, ``108``, ``111``.
Chaque code ci-dessus représente une lettre de [c "hello"]. Par exemple, il y a deux codes [c 108] pour les deux lettres 'l'. (Note~ : l'ordre réel de ces caractères dépend de la plateforme et même du document duquel ces valeurs font partie. Les codes ci-dessus sont dans l'ordre dans lequel ils apparaissent dans la chaîne.)
Les codes de la table ASCII ont ensuite été écrits sur 8 bits pour donner la table ASCII Étendue. La table ASCII Étendue contient 256 valeurs distinctes.
]
[ = Pages de codes IBM
IBM Corporation a défini un ensemble de tables, chacune d'elle affectant les codes de 128 à 256 de la table ASCII étendue à un ou plusieurs systèmes d'écriture. Ces tables de codes ont permis de prendre en charge les lettres de beaucoup plus d'alphabets. Par exemple, les lettres spéciales de l'alphabet turc font partie de la page de codes IBM 857.
Bien qu'elles soient plus utiles qu'ASCII, les pages de codes ont des problèmes et des limitations~ : pour afficher le texte correctement, la page de codes utilisée pour l'écrire doit être connue. En effet, le même code correspond à un autre caractère dans la plupart des autres tables. Par exemple, le code qui représente 'Ğ' dans la table 857 correspond à 'ª' dans la table 437.
Un autre problème est la limitation du nombre d'alphabet qui peuvent être utilisés au sein d'un même document. De plus, les alphabets qui ont plus de 128 caractères non ASCII ne peuvent pas être pris en charges par une table IBM.
]
[ = Pages de codes ISO/IEC 8859
Ces pages de codes sont le résultat d'efforts de standardisation internationaux. Elles sont similaires aux pages de codes IBM dans leur manière d'associer des caractères aux codes. Par exemple, les lettres spécifiques à l'alphabet turc apparaissent dans la page de codes 8859-9. Ces tables ont les mêmes problèmes et limitations que les tables d'IBM. Par exemple, le digramme néerlandais ''ij'' n'apparaît dans aucune de ces tables.
]
[ = Unicode
Unicode résout tous les problèmes et les limitations des solutions précédentes. Unicode s'étend à plus de 100 milliers de caractères et symboles de systèmes d'écritures de beaucoup de langages humains, présents et passés (des nouveaux caractères sont constamment passés en revue en vue d'être ajoutés à la table). Chacun de ces caractères a un code unique. Les documents qui sont codés en Unicode peuvent utiliser tous les caractères des différents systèmes d'écriture en même temps sans aucune ambiguité ni limitation.
]
]
[ = Codages Unicode
Unicode associe un unique code à chaque caractère. Comme il y a plus de caractères Unicode que ce que peut contenir une valeur 8~ bits, certains caractères doivent être représentés par au moins deux valeurs 8-bits. Par exemple, le code Unicode de 'Ğ' (``286``) est plus grand que la valeur maximale d'un type 8 bits (255).
[p Note~ : | je vais utiliser 1 et 30 comme les valeurs des deux octets qui représentent Ğ de façon arbitraire. Ils ne sont valides dans aucun codage Unicode mais utiles pour introduire ces codages Unicode. Les valeurs correctes de ces valeurs sont hors de la portée de ce chapitre.]
La manière dont les caractères sont représentés électroniquement est appelé [* codage]. Nous avons vus ci-dessus comment la chaîne [c "hello"] est représentée en ASCII. Nous allons maintant voir les 3 codages Unicode qui correspondent aux types de caractères D.
- [
[** UTF-32]~ : ce codage utilise 32 bits (4 octets) pour chaque caractère Unicode. Le codage UTF-32 de la chaîne [c "hello"] est similaire à son codage ASCII, mais chaque caractère est représenté par 4 octets~ : ``0``, ``0``, ``0``, ``104``~ ; ``0``, ``0``, ``0``, ``101``~ ; ``0``, ``0``, ``0``, ``108``~ ; ``0``, ``0``, ``0``, ``108``~ ; ``0``, ``0``, ``0``, ``111``.
Autre exemple~ : le codage UTF-32 de "aĞ" est ``0``, ``0``, ``0``,``97``~ ; ``0``,``0``, ``1``, ``30``.
[p Note~ : | les valeurs réelles de ces octets sont différentes et l'ordre réel de ces octets peut être différent.]
'a' et 'Ğ' sont représentés par 1 et 2 octets significatifs respectivement, et les valeurs des 5 autres octets sont toutes zéro. Ces zéros peuvent être vus comme des octets de remplissage qui font que chaque caractère Unicode occupe 4 octets.
Pour les documents écrits avec l'alphabet Latin de base, ce codage utilise toujours 4 fois plus d'octets que le codage ASCII. Quand, dans un document donné, la plupart des caractères a des équivalents ASCII, les trois octets de remplissage pour chacun de ces caractères rendent ce codage moins efficace que les autres codages.
D'un autre côté, il y a des avantages à ce que chaque caractère soit toujours représenté par le même nombre d'octets.
]
- [
[** UTF-16] : Ce codage utilise 16 bits (2 octets) pour représenter la plupart des caractères Unicode. Comme 16 bits peuvent représenter environ 65 milliers de valeurs uniques, les autres 35 mille caractères Unicode doivent être représentés par des octets supplémentaires.
Par exemple, [c "aĞ"] est codé avec 4 octets en UTF-16~ : ``0``, ``97`` ; ``1``, ``30``.
Note~ : les valeurs réelles de certains de ces octets sont différentes et l'ordre réel des octets peut être différent.
Comparé à UTF-32, ce codage prend moins d'espace pour la plupart des documents, mais parce qu'il y a des caractères qui sont représentés par plus de deux octets. UTF-16 est plus compliqué à traiter.
]
- [
[** UTF-8] : ce codage utilise un ou plusieurs octets pour chaque caractère. Si un caractère a un équivalent dans la table ASCII, il est représenté par un octet et par le même code que dans la table ASCII. Les autres caractères Unicode sont représentés par 2, 3 ou 4 octets. La plupart des caractères spéciaux des systèmes d'écritures européens font partie du groupe de caractères qui sont représentés par 2 octets.
Pour la plupart des documents, UTF-8 est le codage qui prend le moins de place. Un autre bénéfice d'UTF-8 est que les documents qui ont déjà étés codés en ASCII correspondent directement à leurs codages UTF-8 respectifs. UTF-8 ne gaspille pas d'espace~ : chaque caractère est représenté par des octets significatifs uniquement. Par exemple, le codage UTF-8 de [c "aĞ"] est~ : ``97``, ``1``, ``30``.
[p Note~ : | Les valeurs réelles de ces octets sont différentes et leur ordre peut être différent également.]
]
]
[ = Les types de caractères du D
Il y a trois types de caractères en D. Ces caractères correspondent aux trois codages Unicode mentionnés ci-avant. D'après ce que vous vous souvenez du [[part:types | chapitre sur les Types Fondamentaux]] :
|=Type |= Définition |= Valeur initiale
| [c char] | unité de stockage UTF-8 | ``0xFF``
| [c wchar] | unité de stockage UTF-16 | ``0xFFFF``
| [c dchar] | unité de stockage UTF-32 et point de code Unicode | ``0x0000FFFF``
Comparé à d'autres langages de programmation, les caractères en D peuvent ne pas avoir le même nombre d'octets. Par exemple, parce que Ğ ne peut être représenté que par 2 octets au minimum dans Unicode, il ne rentre pas dans une variable de type [c char]. En revanche, le type [c dchar], faisant 4 octets, peut stocker n'importe quel caractère Unicode.
Même si le D propose ces types utiles, le D ne supporte pas certaines fonctionnalités ésotériques d'Unicode. J'y reviens après.
]
[ = Caractères littéraux
Les littéraux sont des valeurs constantes qui sont écrits dans le code source du programme. En D, les caractères littéraux sont écrits entres apostrophes~ :
[code=d <<<
char letter_a = 'a';
wchar letter_e_acute = 'é';
>>>]
Les guillemets ne sont pas valides pour les caractères parce qu'ils sont utilisés quand on écrit des chaînes, que nous verrons dans deux chapitres. [c 'a'] est un caractère littéral et [c "a"] est une chaîne littérale constituée d'un caractère.
Les variables de type [c char] ne peuvent stocker que des lettres qui sont dans la table ASCII.
Il y a beaucoup de manières d'insérer des caractères dans le code~ :
- Le plus naturellement, en les tapant sur le clavier.
- En les copiant-collant depuis un autre programme ou un autre texte. Par exemple, vous pouvez copier-coller depuis un site Web, ou depuis un programme conçu pour afficher des caractères (on trouve de tels programmes dans la plupart des environnements Linux sous le nom de «~ Table de Caractères~ »).
- [
En utilisant les noms raccourcis des caractères. La syntaxe, pour le faire, est~ : [c \&nom_du_caractere;]. Par exemple, le nom du caractère Euro est [c euro] et peut être écrit dans le programme comme ceci~ :
[code=d wchar currencySymbol = '\&euro;';]
Voir [[http://digitalmars.com/d/2.0/entity.html | la liste de tous les caractères nommés]] qui peuvent être écrits de cette manière.
]
- [
En indiquant les caractères par leur numéro Unicode~ :
[code=d <<<
char a = 97;
wchar Ğ = 286;
>>>]
]
- [
En spécifiant les codes des caractères de la table ASCII soit par ``\valeur_en_octal`` soit par ``\xvaleur_en_hexadécimal``~ :
[code=d <<<
char questionMarkOctal = '\77';
char questionMarkHexadécimal = '\x3f';
>>>]
]
Ces méthodes peuvent également être utilisées pour écrire des caractères dans les chaînes. Par exemple, les deux lignes qui suivent contiennent la même chaîne~ :
[code=d <<<
writeln("Résumé préparation: 10,25€");
writeln("\x52\&eacute;sum\u00e9 pr\u00e9paration: 10,25\&euro;");
>>>]
]
[ = Caractères de contrôle
Certains caractères ne font qu'affecter le format du texte, il n'ont pas de représentation visuelle propre. Par exemple, le caractère de nouvelle ligne, qui indique que la sortie devrait continuer sur une nouvelle ligne, n'a pas de représentation visuelle. De tels caractères sont appelés caractères de contrôle. Les caractères de contrôles sont écrit avec la syntaxe ``\caractère_de_contrôle``.
|= Syntaxe |= Nom |= Effet
| [c \n] | Nouvelle ligne | Déplace l'affichage sur une nouvelle ligne
| [c \r] | retour chariot | Déplace l'affichage au début de la ligne actuelle.
| [c \t] | tab | Déplace l'affichage à la prochaine tabulation
Par exemple, la fonction [c write], qui ne commence pas de nouvelle ligne automatiquement, le ferait pour chaque caractère [c \n]. Chaque occurence du caractère de contrôle [c \n] à l'intérieur du littéral suivant représente le début d'une nouvelle ligne~ :
[code=d write("première ligne\ndeuxième ligne\ntroisième ligne\n"); ]
[output <<<
première ligne
deuxième ligne
troisième ligne
>>>]
]
[ = Guillemet simple et antislash
Le guillemet simple lui-même ne peut pas être écrit à l'intérieur de guillemets simples parce que le compilateur prendrait le deuxième guillemet comme le caractère qui fermerait le premier : ``'''``. Les deux premiers seraient les guillemets ouvrant et fermant, et le troisième serait tout seul, entraînant une erreur de compilation (NdT~ : de plus, n'écrire aucun caractère entre les deux guillemets simple est illégal).
De manière similaire, on ne peut pas écrire ``'\'`` pour désigner le caractère antislash. Comme l'antislash a une signification spéciale, le compilateur prendrait ``\'`` comme un caractère spécial. Le compilateur chercherait ensuite un guillemet fermant et ne le trouverait pas.
Pour éviter ces confusions, le guillemet simple et l'antislash sont [* échappés] par un antislash :
|= caractère |= Syntaxe |= Nom
| ``'`` |``\'`` | guillemet simple
| ``\`` |``\\`` | antislash (contre-oblique)
]
[ = Le module ``std.uni``
Le module ``std.uni`` inclut des fonctions pour manipuler les caractères Unicode. Vous pouvez voir ces fonctions dans la [[http://dlang.org/phobos/std_uni.html | documentation de ce module]].
Les fonctions qui commencent par ``is`` répondent à certaines questions sur les caractères~ : le résultat est [c false] ou [c true] selon que la réponse est non ou oui (respectivement). Ces fonctions sont utiles dans des expressions logiques~ :
- [c isLower]~ : le caractère est-il en minuscule~ ?
- [c isUpper]~ : le caractère est-il en majuscule~ ?
- [c isAlpha]~ : le caractère est-il alphabétique au sens d'Unicode~ ? (une lettre)
- [c isWhite]~ : le caractère est-il blanc~ ? (espace, nouvelle ligne, tabulation, ...)
Les fonctions qui commencent par ``to`` renvoient de nouveaux caractères à partir de caractères existant~ :
- [c toLower]~ : renvoie le caractère minuscule correspondant au caractère donné.
- [c toUpper]~ : renvoie le caractère majuscule correspondant au caractère donné.
Voici un programme qui utilise toutes ces fonctions :
[code=d <<<
import std.stdio;
import std.uni;
void main()
{
writeln("Est-ce que ğ est minuscule ? ", isLower('ğ'));
writeln("Est-ce que Ş est minuscule ? ", isLower('Ş'));
writeln("Est-ce que Ş est minuscule ? ", isUpper('İ'));
writeln("Est-ce que ç est majuscule ? ", isUpper('ç'));
writeln("Est-ce que z est alphanumérique ? ", isAlpha('z'));
writeln("Est-ce que € est alphanumérique ? ", isAlpha('\&euro;'));
writeln("Est-ce que la nouvelle ligne est un caractère blanc ? ", isWhite('\n'));
writeln("Est-ce que le tiret du bas est un caractère blanc ? ", isWhite('_'));
writeln("La minuscule de Ğ : ", toLower('Ğ'));
writeln("La minuscule de İ : ", toLower('İ'));
writeln("La majuscule de ş : ", toUpper('ş'));
writeln("La majuscule de ı : ", toUpper('ı'));
}
>>>]
[output <<<
Est-ce que ğ est minuscule ? true
Est-ce que Ş est minuscule ? false
Est-ce que İ est majuscule ? true
Est-ce que ç est majuscule ? false
Est-ce que z est alphanumérique ? true
Est-ce que € est alphanumérique ? false
Est-ce que la nouvelle ligne est un caractère blanc ? true
Est-ce que le tiret du bas est un caractère blanc ? false
La minuscule de Ğ : ğ
La minuscule de İ : i
La majuscule de ş : Ş
La majuscule de ı : I
>>>]
[ = Prise en charge limitée pour ı et i de l'alphabet turc
Les versions minuscules et majuscules des lettres ı et i sont «~ pointées~ » ou non de façon cohérente dans l'alphabet turc. La majorité des alphabets sont incohérents de ce point de vue~ : la majuscule du i «~ pointé~ » n'est pas «~ pointée~ ».
Parce que les systèmes informatiques ont commencé avec la table ASCII, la majuscule de ``i`` est ``I``. Pour cette raison, ces deux lettres ont besoin d'une attention particulière. Le programme suivant montre ce problème~ :
[code=d <<<
import std.stdio;
import std.uni;
void main()
{
writeln("La majuscule de i : ", toUpper('i'));
writeln("La minuscule de I : ", toLower('I'));
}
>>>]
La sortie correspond à l'alphabet Latin~ :
[output <<<
La majuscule de i : I
La minuscule de I : i
>>>]
]
[ = Tous les alphabets ont une prise en charge limitée
Les caractères sont transformés en majuscule / minuscule selon leur code Unicode. Cette méthode est problématique pour beaucoup d'alphabets. Par exemple, les alphabets azéri et celte sont aussi affectés par le problème de la minuscule [c 'I'] étant [c 'i'].
Il y a des problèmes similaires avec le tri. Les lettres de beaucoup d'alphabet, comme le ğ en turc, sont placées après le z. Même les caractères accentués comme á sont placés après le z, et ce, même pour l'alphabet latin basique.
]
]
[ = Problème de lecture des caractères
La souplesse et la puissance des caractères Unicode en D peuvent être la source de résultats inattendus lors de la lecture d'un flux d'entrée. Cette contradiction est due aux multiples sens du terme «~ caractère~ ». Avant de développer plus ce point, considérons un programme qui a ce problème~ :
[code=d <<<
import std.stdio;
void main()
{
char lettre;
write("Veuillez entrer une lettre : ");
readf(" %s", &lettre);
writeln("La lettre suivante a été lue : ", lettre);
}
>>>]
Si vous essayez ce programme dans un environnement qui n'utilise pas Unicode, vous pouvez voir que même les caractères non Unicode sont lus et affichés correctement.
Cependant, si vous démarrez ce programme dans un environnement Unicode, par exemple une console sous Linux, vous pouvez voir que le caractère affiché à la sortie n'est pas le même caractère que celui qui a été entré. Pour voir ceci, entrons un caractère non-ASCII dans une console qui utilise le codage UTF-8 (comme la plupart des consoles sous Linux)~ :
[output <<<
Veuillez entrer une lettre : ğ
La lettre suivante a été lue : ← Pas de lettre à la sortie
>>>]
La raison de ce problème est que les charactères non ASCII tels que le ğ sont représentés par deux codes, et lire un [c char] depuis l'entrée ne lit que le premier des deux octets. Comme ce seul caractère n'est pas suffisant pour représenter le caractère Unicode entier, la console n'affiche pas le caractère incomplet.
Pour voir que les codes UTF-8 qui constituent le caractère sont lus [c char] par [c char], lisons deux [c char] et affichons-les l'un après l'autre~ :
[code=d <<<
import std.stdio;
void main()
{
char premierCode;
char secondCode;
write("Veuillez entrer une lettre : ");
readf(" %s", &premierCode);
readf(" %s", &secondCode);
writeln("La lettre suivante a été lue : ",
premierCode, secondCode);
}
>>>]
Le programme lit deux variables [c char] depuis l'entrée et les affiche dans le même ordre. Quand ces codes sont envoyés à la console dans le même ordre, ils correspondent à un caractère UTF-8 entier sur la console et cette fois, le caractère Unicode est affiché correctement~ :
[output <<<
Veuillez entrer une lettre : ğ
La lettre suivante a été lue : ğ ← Le caractère Unicode qui
consiste en deux codes char
>>>]
Ces résultats sont dépendant aussi du fait que les entrées-sorties standard des programmes sont des flux de [c char].
Nous verrons plus tard dans le [[part:chaines | chapitre sur les chaînes]] qu'il est plus facile de lire des caractères commes des chaînes, plutôt que s'occuper individuellement des codes UTF-8.
]
[ = Prise en charge de l'Unicode par le langage D
Unicode est un standard vaste et compliqué. D prend en charge un sous-ensemble utile de celui-ci.
Dans un document codé en Unicode, on distingue plusieurs idées~ :
- [
[* Unité de stockage]~ (''code unit'')~ : les valeurs qui constituent les codages UTF sont appelées unités de stockage. Selon le codage et les caractères eux-même, les caractères Unicode sont faits de une ou plusieurs unités de stockage. Par exemple, dans le codage UTF-8, la lettre 'a' est faite d'une seule unité de stockage et la lettre 'ğ' est faite de deux unités de stockage.
Les types [c char], [c wchar], et [c dchar] du D correspondent respectivement à une unité de stockage UTF-8, UTF-16, et UTF-32.
]
- [
[* Point de code] (''code point'')~ : chaque lettre, chiffre, symbole, etc. qu'Unicode définit est appelé un point de code. Par exemple, les codes Unicode de 'a' est de 'ğ' sont deux points de code distincts.
Selon le codage, les points de code sont représentés par une ou plusieurs unités de stockage. Comme mentionné ci-avant, dans le codage UTF-8 'a' est représenté par une unité de stockage et 'ğ' est représenté par deux unités de stockage. En revanche, 'a' et 'ğ' sont représentés par une seule unité de stockage dans les codages UTF-16 et UTF-32.
Le type D qui prend en charge les points de code est [c dchar]. [c char] est [c wchar] ne peuvent être utilisés que pour les unités de stockage.
]
- [
[* Caractère] : n'importe quel symbole que le standard Unicode définit et que nous appelons «~ caractère~ » dans la vie de tous les jours est un caractère.
La définition Unicode de caractère est flexible et ceci complique les choses. Certains caractères peuvent être formés de un ou plusieurs points de codes. Par exemple, la lettre ğ peut être désignée de deux manières~ :
- comme simple point de code 'ğ'~ ;
- comme les deux points de codes 'g' et '̆ '. ([[http://www.fileformat.info/info/unicode/char/306/index.htm|combining breve]])
D ne prend pas nativement en charge le concept de points de codes combinés. En D, le point de code ğ est différent des deux points de codes consécutifs 'g' et '̆ '.
]
]
[ = Résumé
- Unicode supporte tous les caractères de tous les systèmes d'écritures.
- [c char] est pour le codage UTF-8~ ; même s'il ne convient pas pour représenter les caractères en général, il prend en charge la table ASCII.
- [c wchar] est pour l'encodage UTF-16~ ; même s'il ne convient pas pour représenter les caractères en général, il peut prendre en charge une multitude d'alphabets.
- [c dchar] est pour le codage UTF-32~ ; il peut également être utilisé pour les points de codes parce qu'il est 32 bits.
]