|
- Données binaires
- ================
-
- Introduction : chiffres et nombres
- -----------------------------------
-
- Si je vous parle de ce que représente le texte: ``342`` vous pouvez le
- voir de deux façons:
-
- 1. C'est une **suite de chiffres**: ``3``, puis ``4``, puis ``2``.
- 1. C'est un **nombre** (quelque part entre 300 et 350)
-
- On se sert des *chiffres* de 0 à 9 pour *représenter* des *nombres*
-
- La base 10
- ----------
-
- Plus exactement, pour passer de la suite de chiffres ``342`` au nombre,
- on part de la fin, puis on ajoute chaque chiffre, multiplié par la puissance
- de 10 adéquate::
-
- 2 * 1
- + 4 * 10
- + 2 * 10 * 10
-
- soit::
-
- 2
- + 40
- + 300
-
- ce qui fait bien 342.
-
-
- La base 16
- ----------
-
- En informatique, on se sert souvent de la base 16. C'est le même principe: on se
- sert des "chiffres" de 0 à F (A vaut 10, B vaut 11, jusqu'à F qui vaut 15)
-
- Ainsi, la suite ``DA2`` peut être interprétée comme suit ::
-
- 2 * 1
- + 10 * 16
- + 13 * 16 * 16
-
- soit ::
-
- 2
- + 160
- + 3328
-
- soit 3746
-
- On appelle aussi la base 16 la base *hexadécimale*, ou *hexa* en abrrégé.
-
- La base 2
- ----------
-
- La base 2 c'est pareil, mais avec deux "chiffres" - 0 et 1.
-
- Ainsi, la suite `110` peut être interprétée comme suit ::
-
- 0 * 1
- + 1 * 2
- + 1 * 2 * 2
-
- soit ::
-
- 0
- + 2
- + 4
-
- soit 6.
-
-
- Bits et octets
- --------------
-
- * Un bit (*bit* en anglais) c'est la valeur 1 ou 0
- * Un octet (*byte* en anglais) c'est une suite de 8 bits
-
- À retenir
- ---------
-
- **Ces paquets de 8 ne veulent rien dire en eux-mêmes**.
- Ils n'ont de sens que dans le cadre d'une *convention*.
-
- Par exemple, l'octet '10100100' peut être un nombre écrit en
- binaire (164 en l'occurrence), mais peut avoir une toute
- autre signification
-
- Le nombre de valeurs possible augmente *très* rapidement avec le nombre d'octets:
-
- * 1 octet, c'est 255 valeurs possibles (``2 ** 8``)
- * 2 octets, c'est 65.536 valeurs possibles (``2 ** 16``)
- * 4 octets, c'est 4.294.967.296 valeurs possibles (``2 ** 32``)
-
- Bases en Python
- ---------------
-
- On se sert des préfixes `0b` et `0x` pour noter
- les nombres en base binaire ou hexadécimale respectivement::
-
- print(0b1110)
- # affiche: 6
-
- print(0xDA2)
- # affiche: 3490
-
- Poids des bits
- --------------
-
- Regardez l'example suivant::
-
- x = 0b0010010 # 18
- y = 0b0010011 # 19
- z = 0b1010010 # 82
-
- Notez que le premier bit est plus "fort" que le dernier on dit qu'on est en "little endian".
-
-
- Manipuler des octets en Python
- ------------------------------
-
- On peut construrie des listes d'octets en utilsant ``bytearray`` et
- une liste de nombres::
-
- data = bytearray(
- [0b1100001,
- 0b1100010,
- 0b1100011
- ]
- )
-
- # equivalent:
- data = bytearray([97,98,99])
-
- # equivalent aussi:
- data = bytearray([0x61, 0x62, 0x63]
-
-
- Texte
- -----
-
- On peut aussi interpréter des octets comme du texte - c'est la table ASCII
-
- .. image:: ../img/ascii-table.png
-
-
- ASCII - remarques
- -----------------
-
- * C'est *vieux* - 1960
- * Le A est pour American
- * Ça sert à *envoyer* du texte sur des terminaux d'où les "caractères" non-imprimables dans la liste
- * Mais c'est une convention *très* utilisée
- * Techniquement, on n'a besoin que de 7 bits, mais on préfère envoyer des octets
-
- Utiliser ASCII en Python
- ------------------------
-
- Avec ``chr`` et ``ord``::
-
- x = chr(98)
- print(x)
- # affiche: b
-
- x = ord('a')
- print(x)
- # affiche: 97
-
- Affichage des bytearrays en Python
- ----------------------------------
-
- Python utilise ASCII pour afficher les bytearrays si les caractères sont "imprimables"::
-
- data = bytearray([97,98,99])
- print(data)
- # affiche: bytearray(b"abc")
-
- Et ``\x`` et le code hexa sinon::
-
- data = bytearray([7, 69, 76, 70])
- print(data)
- # affiche: bytearray(b"\x07ELF")
-
- Notez bien que ce qu'affiche Python n'est qu'une *interpétation* d'une séquence d'octets.
-
- Types
- -----
-
- La variable `b"abc"` est une "chaîne d'octets", de même que `"abc"` est une "chaîne de caractères".
-
- Python apelle ces types `bytes` et `str`::
-
- print(type("abc"))
- # affiche: str
-
- print(type(b"abc"))
- # affiche: bytes
-
-
- bytes et bytearray
- ------------------
-
- De la même manière qu'on ne peut pas modifier un caractère à l'intérieur une string, on ne peut
- pas modifier un bit - ou un octet dans une variable de type `bytes`::
-
- a = "foo"
- # a[0] = "f" => TypeError: 'str' object does not support item assignment
-
- b = b"foo"
- # b[0] = 1 => TypeError: 'bytes' object does not support item assignment
-
- Par contre on peut modifier un bytearray::
-
- b = bytearray(b"foo")
- b[0] = 103
- print(b)
- # affiche: bytearray(b"goo")
-
- Conversion bytes - texte
- ------------------------
-
- Avec ``encode()`` et ``decode()``::
-
- text = "chaise"
- encodé = text.encode("ascii")
- print(encodé)
- # affiche: b"chaise"
-
- bytes = b"table"
- décodé = bytes.decode("ascii")
- print(décodé)
- # affiche: b"table"
-
-
- Notez que dans le deuxième exemple, on est bien en train de "décoder"
- un paquet de 0 et de 1. Il peut s'écrire ainsi:
-
-
- bytes = b"\x74\x61\x62\x6c\x65"
- décodé = bytes.decode("ascii")
- print(décodé)
- # affiche: table
-
- Plus loin que l'ASCII
- ---------------------
-
- Vous avez sûrement remarquer qu'il n'y a pas de caractères accentués dans
- ASCII. Du coup, il existe d'autres *conventions* qu'on appelle "encodage".
-
- On peut spécifier l'encodage quand on appelle la méthode ``decode()``::
-
- # latin-1: utilisé sur certains vieux sites
- data = bytearray([233])
- lettre = data.decode('latin-1')
- print(lettre)
- # affiche: 'é'
-
- # cp850: dans l'invite de commande Windows
- data = bytearray([233])
- lettre = data.decode('cp850')
- print(lettre)
- # affiche: 'Ú'
-
- Notez que la même suite d'octets a donné des résultats différents en fonction
- de l'encodage!
-
- Unicode
- --------
-
- L'Unicode c'est deux choses:
-
- 1. Une **table** qui associe un un "codepoint" à chaque caractère
- 2. Un encodage particulier, l'UTF-8, qui permet de convertir une suite
- d'octets en suite de codepoint et donc de caractères
-
- UTF-8 en pratique
- ------------------
-
- D'abord, UTF-8 est compatible avec ASCII::
-
- encodé = "abc".encode("utf-8")
- print(encodé)
- # Affiche: b'abc'
-
- Ensuite, certains caractères (comme ``é``) sont représentés par 2 octets::
-
- encodé = "café".encode("utf-8")
- print(encodé)
- # Affiche: b'caf\xc3\xa9"
-
-
- Enfin, certains caractères (comme les emojis) sont représentés par pleins d'octets::
-
- encodé = "I ❤️ you".encode("utf-8")
- print(encodé)
- b'I \xe2\x9d\xa4\xef\xb8\x8f you'
-
-
- .. warning::
-
- Toutes les séquences d'octets ne sont pas forcément valides quand on veut
- les décoder en UTF-8
-
- Conséquences
- -------------
-
- * On peut représenter *tout* type de texte avec UTF-8 (latin, chinois, coréen, langues disparues, ....)
- * On ne peut pas accéder à la n-ème lettre directement dans une chaîne
- encodée en UTF-8, il faut parcourir lettre par lettre (ce qui en pratique est rarement
- un problème).
|