Skip to article frontmatterSkip to article content

le type bytes

non mutable

b = bytes([65, 66, 67])
b
b'ABC'
# non mutable !
try:
    b[1] = 68
except Exception as exc:
    print(f"OOPS - {type(exc)}\n{exc}")
OOPS - <class 'TypeError'>
'bytes' object does not support item assignment

littéral

pour construire un bytes on peut soit

# caractère -> code ASCII
b = b'AB\n'
b
b'AB\n'
# ou sinon en hexa
b'\x41\x42\x0a'
b'AB\n'

un bytes est une séquence

# les méthodes dans `str` mais pas dans `bytes`

set(dir(str)) - set(dir(bytes))
{'casefold', 'encode', 'format', 'format_map', 'isdecimal', 'isidentifier', 'isnumeric', 'isprintable'}
# les méthodes dans `bytes` mais pas dans `str`

set(dir(bytes)) - set(dir(str))
{'__buffer__', '__bytes__', 'decode', 'fromhex', 'hex'}

texte, binaire et encodage

codage et décodage en python

le problème

de multiples encodages

Unicode

codepoints et 3 encodages

rappels: hexadecimal

# an int, entered in hexa, printed in decimal
i1 = 0xe8; i1
232
# or printed in binary
bin(i1)
'0b11101000'
# a bytes with one byte
b = b'\xe8'

# and so its only value as an int is
i2 = b[0]

# and there's the same indeed
i1 == i2
True

UTF-8

Unicode et Python: chr et ord

# le codepoint du é accent aigu
codepoint = 0xe9
codepoint
233
chr(codepoint)
'é'
ord('é')
233

Martine écrit en UTF-8

pourquoi l’encodage c’est souvent un souci ?

voyons comment on en arrive par exemple à recevoir un mail en gloubli-goulba

# Jean écrit un mail

envoyé = "Martine écrit en UTF-8"
# son OS l'encode pour le faire passer sur le réseau

binaire = envoyé.encode(encoding="utf-8")
# Pierre reçoit le binaire mais son ordi
# est un vieux Windows mal configuré

reçu = binaire.decode(encoding="cp1252")
# Pierre voit ceci dans son mailer
reçu
'Martine écrit en UTF-8'

mais le plus souvent ça marche !

comment en est on arrivé là ?

encodages par défaut

# on connait l’encodage du terminal avec
import sys
sys.stdin.encoding
'utf-8'
# et dans l'autre sens
#
sys.stdout.encoding
'UTF-8'
# on connait l’encodage du système de fichier avec
sys.getfilesystemencoding()
'utf-8'
# et le réglage système par défaut
sys.getdefaultencoding()
'utf-8'

en principe, au 21-ème siècle vous devez avoir utf-8 partout !

partie pour les avancés

décodage dégradé

octets = 'un été, à noël'.encode(encoding='utf-8')
octets.decode('ascii', errors='ignore')
'un t, nol'
octets.decode('ascii', errors='replace')
'un ��t��, �� no��l'

encodage dégradé

s = 'un été, à noël'
s.encode('ascii', errors='ignore')
b'un t, nol'
s.encode('ascii', errors='replace')
b'un ?t?, ? no?l'

Unicode vers ASCII

deviner l’encodage ?

!pip install chardet
Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: chardet in /usr/lib/python3/dist-packages (5.2.0)
!chardetect ../data/une-charogne.txt
../data/une-charogne.txt: utf-8 with confidence 0.99

à savoir : le BOM

le BOM consiste à ajouter un header pour utf-16 et utf-32
qui crée une inflation artificielle

# en UTF-32:  1 char = 4 bytes
# donc on devrait voir 4
len("a".encode('utf32'))
8
# les 4 premiers octets correspondent
# à la constante 'UTF32-LE'
b = "a".encode('utf32')
b[:4]
b'\xff\xfe\x00\x00'
# évidemment ce n'est ajouté qu'une seule fois
s1000 = 1000*'x'
len(s1000.encode('utf32'))
4004

petit retour sur le type str

les chaines littérales

lorsqu’on veut écrire directement dans le programme
une chaine avec des caractères exotiques

# entré directement au clavier
accent = 'é'
accent
'é'
# copié collé
warn = '⚠'
warn
'⚠'
# défini à partir de son codepoint
# si petit (un octet), format hexadécimal
'\xe9'
'é'
# si plus grand, utiliser \u
# pour les codepoints sur 2 octets
"\u26A0"
'⚠'

et enfin, utilisez \Uxxxxxxxx pour 4 octets, si codepoint encore plus grand (pas fréquemment utile)

un exemple

s = '\u0534\u06AB\u05E7\u098b\u0bf8\u0f57\u2fb6'
print(s)
Դګקঋ௸བྷ⾶

avec ces trois notations '\x \uet\u` il faut bien sûr utiliser exactement, respectivement, 2, 4 ou 8 digits hexadécimaux.

# le retour chariot a pour code ASCII 10
print('\x0a')


# je ne peux pas faire l'économie du 0
try:
    print('\xa') # python n'est pas content
except:
    import traceback; traceback.print_exc()
  Cell In[36], line 3
    print('\xa') # python n'est pas content
          ^
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-2: truncated \xXX escape

le type bytearray (avancé)

source = b'spam'
buff = bytearray(source)
buff
bytearray(b'spam')
# remplacer 'a' bar 'e'
buff[2] = ord('e')
buff
bytearray(b'spem')
for char in buff:
    print(char, end=" ")
115 112 101 109 

méthodes sur bytearray

# méthode dans bytes
# mais pas dans bytearray
set(dir(bytes)) - set(dir(bytearray))
{'__bytes__', '__getnewargs__'}
# méthode dans bytearray
# mais pas dans bytes
set(dir(bytearray)) - set(dir(bytes))
{'__alloc__', '__delitem__', '__iadd__', '__imul__', '__release_buffer__', '__setitem__', 'append', 'clear', 'copy', 'extend', 'insert', 'pop', 'remove', 'reverse'}