Représentation des nombres et caractères 1 Conversion de bases

Transcription

Représentation des nombres et caractères 1 Conversion de bases
Univ. Lille 1 - Licence Informatique 2ème année
2016-2017
Codage de l’information
Représentation des nombres et caractères
Objectifs du TP
Ce TP a pour but
1. d’étudier la programmation des conversions entiers <-> chaînes de caractères ;
2. d’étudier la représentation des nombres flottants ;
3. et d’étudier la représentation des caractères ISO-8859-1 et UTF-8.
Outils utilisés
— le langage Python ;
— le programme iconv ;
— le programme diff
La documentation des différentes fonctions à réaliser est disponible à l’adressehttp://www.fil.univ-lille1.fr/~sals
1
Conversion de bases
Il s’agit dans cette partie de réaliser des fonctions permettant de convertir des entiers décimaux
dans n’importe quelle base.
Python offre déjà certaines possiblités de conversion que nous allons commencer par essayer.
1.1
Impression des entiers avec print
Tous les langages de programmation offrent au programmeur la possibilité d’imprimer les nombres
entiers. Python n’échappe pas à la règle, avec la procédure print.
>>> print (31415)
31415
La procédure print imprime l’entier 31415 en base décimale.
Il est aussi possible de demander à la procédure print d’imprimer les entiers en base 8 ou en base
16. Il faut pour cela utiliser la méthode format de la classe str. Par exemple pour transformer un
entier en une chaîne contenant la représentation hexadécimal, on utilisera "{:x}".format(31415).
>>> print ( " {: x } " . format (31415))
7 ab7
Question 1 Imprimez les entiers de votre choix avec la procédure print et les formats d’impression {:x},
{:o}, {:X}. Expliquez la différence entre chacun de ces formats.
Python dispose aussi des fonctions bin, oct, hex qui prennent en paramètre un entier et qui
retournent une chaîne de caractères correspondant respectivement à la représentation binaire, octale
et hexadécimale de l’entier.
Question 2 Convertissez l’entier 1331 en utilisant chacune de ces trois fonctions.
1
On se propose maintenant d’écrire des fonctions pour convertir un entier dans n’importe quelle
base.
1.2
Transformer un entier en un chiffre
Question 3 Que vaut l’expression chr(ord(’0’) + n) lorsque n est un entier compris entre 0 et 9 ? et si
n ≥ 10 ?
Question 4 Si n désigne un entier compris entre 10 et 15 inclus, quelle expression, dépendant de n et
utilisant les deux fonctions chr et ord, donne un caractère compris entre ’A’ et ’F’ avec la correspondance
10 -> ’A’, . . . , 15 -> ’F’ ?
Question 5 Réalisez une fonction nommée integer_to_digit qui pour un entier compris entre 0 et 15
renvoie le chiffre hexadécimal correspondant (sous forme de caractère).
Voici des exemples de sortie de votre fonction integer_to_digit.
>>> integer_to_digit (15)
’F ’
>>> integer_to_digit (0)
’0 ’
Notez qu’il serait particulièrement judicieux d’intégrer ces exemples dans la documentation de votre
fonction Python. Faîtes-le également quand aucun exemple n’est fourni. Dans ce cas c’est à vous d’abord
de calculer le résultat attendu par la fonction, puis de coder votre fonction.
Notez également à toujours tester que les paramètres fournis remplissent les conditions attendues à
l’aide de la fonction assert.
1.3
Convertir un entier en une chaîne de caractères
Question 6 Réalisez une fonction nommée integer_to_string qui pour deux entiers n et b renvoie l’écriture de l’entier n en base b.
Question 7 Utilisez la fonction que vous venez de réaliser, ainsi que la procédure print avec les seuls
formats {:d} et {:s} (impression de chaînes), pour afficher sous forme d’un tableau les écritures décimales,
binaires, octales et hexadécimales des entiers de 0 à 20.
L’affichage doit avoir la forme
0
1
2
3
4
5
:
:
:
:
:
:
0
1
10
11
100
101
0
1
2
3
4
5
0
1
2
3
4
5
2
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
2
110
111
1000
1001
1010
1011
1100
1101
1110
1111
10000
10001
10010
10011
10100
6
7
10
11
12
13
14
15
16
17
20
21
22
23
24
6
7
8
9
A
B
C
D
E
F
10
11
12
13
14
Opérations logiques sur les entiers
2.1
Les opérateurs logiques sur les entiers en Python
Les opérateurs logiques sur les entiers sont prédéfinis en Python.
— & : opération et bit à bit.
— | : opération ou bit à bit.
— ^ : opération ou exclusif bit à bit.
— ~ : opération non bit à bit.
— << : décalage à gauche.
— >> : décalage à droite.
Question 8 Testez chacun de ces opérateurs dans un interpréteur du langage.
Question 9 Quelle est la signification arithmétique de l’opération logique n << 1 ? Et de n >> 1 ?
Question 10 Réalisez une fonction deux_puissance qui prend en paramètre un entier n ≥ 0 et qui renvoie
la valeur de 2n en utilisant uniquement un opérateur logique.
Question 11 Comment avec les opérations logiques peut-on tester si un entier est pair ?
2.2
Conversion en base 2
Il s’agit ici de se concentrer sur la conversion d’un entier en une chaîne binaire et réciproquement.
Question 12 Sans utiliser d’opérateurs arithmétiques, réalisez la fonction integer_to_binary_str qui
renvoie l’écriture binaire (sous forme de chaîne de caractère) de l’entier passé en paramètre.
3
Question 13 Sans utiliser d’opérateurs arithmétiques, réalisez la fonction binary_str_to_integer qui à
partir d’une écriture binaire renvoie l’entier qui lui correspond.
3
Représentation des flottants
Dans un interpréteur Python, importez le module struct. Tapez l’instruction bytes_stored = struct.pack(’>f’,
Cette instruction a pour effet de créer un objet de type bytes qui contient la représentation sous
forme de 4 octets du réel 3,5.
Il est possible d’accéder à chacun des octets de la même manière que dans une liste.
Question 14 Réalisez une fonction byte_to_binary qui prend en paramètre un octet qui renvoie sa représentation binaire sur 8 bits.
Question 15 Faîtes une fonction float_to_bin qui prenne en paramètre un réel et qui renvoie une chaîne
binaire correspondant au réel stocké. Cette fonction utilisera bien entendu la méthode struct.pack.
Exemple :
>>> float_to_bin (3.5)
’ 01000000011000000000000000000000 ’
Nous allons maintenant voir l’impact d’un changement de bit dans un réel.
Question 16 Pour cela nous allons commencer par faire une fonction change_a_bit qui prend en paramètre
une chaîne binaire et qui renvoie une chaîne binaire dans laquelle, à la position donnée en paramètre, le
caractère, qui représente un bit, a été remplacé par le bit inverse.
Question 17 Réalisez une fonction binary_to_bytes qui prend une chaîne binaire en entrée et qui la
convertit, 8 bits par 8 bits, en une liste d’entiers.
>>> binary_to_bytes ( ’ 11 0 1 01 1 0 11 0 1 01 1 1 11 0 1 1 00 0 ’)
[214 , 215 , 216]
Question 18 Faîtes une fonction change_a_bit_in_float qui prend en paramètre un réel ainsi que la
position à modifier dans la représentation binaire et qui renvoie la valeur du réel modifié.
Pour cela, sachez que struct.unpack(’>f’, bytes([64, 96, 0, 0])) retourne un tuple dont le premier élément est le réel représenté par les 4 octets 64, 96, 0 et 0.
Question 19 Prenons le réel 2. Quelle modification implique une modification du premier bit ? Du dernier
bit ? Du neuvième bit ? Pourquoi ?
4
4
Lecture et écriture de fichiers
Matériel fourni Un fichier texte nommé data, contenant un caractère UTF-8.
4.1
Deux modes d’ouverture des fichiers
Un fichier peut être lu dans deux modes différents avec Python. Soit il s’agit d’un fichier texte
et dans ce cas Python lira, et renverra, des caractères (qu’ils soient stockés sur 1, 2, 3 ou 4 octets),
soit il s’agit d’un fichier dit binaire (bien que cette appellation ait peu de sens, car les données sont
toujours représentées en binaire) et dans ce cas Python lira et renverra des octets.
Nous allons lire un fichier en utilisant ces deux modes pour bien cerner la différence.
Le mode de lecture est déterminé par le second paramètre passé à la fonction open. Le mode de
lecture par défaut est le mode texte. Pour lire (ou écrire un fichier) en mode binaire, il faut ajouter
un b à la chaîne de caractères correspondant au mode d’ouverture du fichier. Donc pour ouvrir
un fichier en lecture en mode binaire, il faut utiliser le mode rb et pour l’ouvrir en mode écriture
binaire il faut utiliser le mode wb.
4.2
Lecture de fichier
Question 20 Ouvrez le fichier data en lecture en mode texte ainsi qu’en mode binaire. Vous stockerez le
résultat de la fonction open dans deux variables stream_text et stream_bin respectivement pour le fichier
ouvert en mode texte et en mode binaire.
Ces deux variables correspondent à flux ouvert en lecture. Il est ensuite possible de lire un tel flux avec
la méthode read (que vous avez déjà utilisée dans d’autres contexte a priori). De même, il est possible
d’écrire un flux (s’il a été ouvert en écriture), avec la méthode write. Un flux peut être, comme ici, un
fichier ouvert mais également une connexion réseau voire une chaîne de caractères. Le flux permet justement
de s’abstraire de la manière dont il a été obtenu. Dans ce TP, et dans les suivants, nous passerons souvent
des flux en paramètre. Gardez donc bien cela en tête.
Question 21 Lisez tout le flux en un seul appel à la méthode read() et stockez le résultat dans une
variable content_text (pour le flux ouvert en mode texte) et une variable content_bin (pour le flux ouvert
en mode binaire). Donnez la longueur lue pour les deux modes de lecture. Expliquez les résultats obtenus.
Question 22 Quel est le type de la variable content_bin ?
Question 23 Comment accéder au deuxième octet lu en mode binaire ?
4.3
Écriture de fichier
Nous allons maintenant écrire un fichier en mode binaire. Là encore le nom du mode est ambigu.
Cela ne signifie pas que nous allons écrire dans le fichier bit à bit, au contraire la fonction d’écriture
prend en entrée des octets.
Par exemple si output est un flux ouvert en écriture en mode binaire, output.write(bytes([65, 66]))
écrit les octets 65 et 66 dans le fichier (ce qui correspond aux caractères A et B).
Question 24 Ouvrir un nouveau fichier (nommé data.out) en écriture en mode binaire et écrire à l’intérieur
les octets 195 et 137. Il faut ensuite penser à fermer le fichier.
Question 25 Ouvrez maintenant le fichier avec un éditeur de texte (ce qui revient à l’ouvrir en mode
5
texte). Combien de caractères possède-t-il ? Comment l’expliquez-vous ?
5
Représentation des caractères ISO-859-1 et UTF-8
Matériel fourni
— Un fichier texte au format ISO-8859-1 contenant la fable « La Cigale et la Fourmi » ;
— un fichier texte au format UTF-8 contenant la fable « La Cigale et la Fourmi » ;
— la table des caractères ISO-8859-1 ;
— le principe du code UTF-8.
5.1
La commande iconv
Sous Unix, la commande iconv permet de convertir un fichier d’un codage dans un autre.
Ainsi, pour convertir un fichier texte du format ISO-8859-1 dans le format UTF-8, on utilise la
commande
> iconv --from-code ISO-8859-1 --to-code UTF-8 --output fichier_converti fichier_a_convertir
où fichier_converi est le nom du fichier résultat et fichier_a_convertir est le nom du fichier
que l’on désire convertir.
Pour connaître la liste de tous les codages connus par cette commande
$ iconv --list
Le but du TP est de réaliser deux programmes l’un pour convertir de l’ISO-8859-1 vers l’UTF-8,
l’autre pour la conversion dans l’autre sens (si elle est possible !).
Question 26 Utilisez la commande iconv pour
1. convertir le fichier cigale-ISO-8859-1.txt en un fichier équivalent codé en UTF-8 ;
2. convertir le fichier cigale-UTF-8.txt en un fichier équivalent codé en ISO-8859-1.
Vérifiez le résultat
— en visualisant le contenu des deux fichiers obtenus à l’aide d’un éditeur de textes ;
— puis en comparant les fichiers obtenus avec ceux qui vous sont fournis. Pour cela utilisez la commande
diff ;
> diff cigale - ISO -8859 -1. txt moncigale - ISO -8859 -1. txt
— et enfin en utilisant la commande file ;
> file cigale - UTF -8. txt
cigale - UTF -8. txt : UTF -8 Unicode text
> file cigale - ISO -8859 -1. txt
cigale - ISO -8859 -1. txt : ISO -8859 text
Question 27 Comparez les tailles (exprimées en octets) des deux fichiers cigale-ISO-8859-1.txt et
cigale-UTF-8.txt. Expliquez la différence constatée. Pour connaître la taille d’un fichier, utilisez la commande ls avec l’option -l. Par exemple, pour le fichier cigale-UTF-8.txt on obtient
6
> ls -l cigale - UTF -8. txt
-rw -r - -r - - 1 eric eric 639 2010 -09 -29 07:12 cigale - UTF -8. txt
qui montre que le fichier a une taille de 639 octets.
5.2
Conversion d’un fichier du format ISO-8859-1 au format UTF-8
La conversion d’un fichier texte du format ISO-8859-1 au format UTF-8 peut se faire simplement
par lecture du fichier de départ octet par octet, et en écrivant dans le fichier d’arrivée un ou deux
octets selon les cas suivants :
1. si l’octet lu a un bit de poids fort nul, c’est un caractère ASCII et on le recopie tel quel dans
le fichier d’arrivée ;
2. sinon, l’octet a une valeur comprise entre 160 et 255, et on doit recopier deux octets dans le
fichier d’arrivée
110xxxxx 10xxxxxx
où les 11 x sont à remplacer par les bits de l’octet lu (les trois premiers x étant des 0). Par
exemple, le caractère É a pour code C9=11001001 en ISO-8859-1. Son code en UTF-8 est
11000011 10001001 = C3 89.
Question 28 Comment transformer un octet de valeur comprise entre 160 et 255 en deux octets conformes
à la description donnée plus haut, uniquement en utilisant des opérations logiques sur les entiers ? (cf. TD)
Question 29 Écrivez une fonction en Python nommée isolatin_to_utf8 qui prend en paramètre un
flux d’entrée, qui lit un caractère ISO-8859-1 dans ce flux et qui renvoie un tuple d’un ou deux octets
correspondants au caractère UTF-8.
Votre fonction devra uniquement utiliser des opérations logiques, pas d’opérations arithmétiques et
surtout pas de chaînes de caractères. La conversion en chaîne de caractère serait très coûteuse et il est
possible d’arriver au même résultat beaucoup plus efficacement en ayant recours aux opérations logiques.
Pour lire octet par octet le flux donné en paramètre, nous utiliserons la méthode read comme ceci :
entree.read(1)[0], en supposant que le paramètre est appelé entree. Quelques rappels de la partie précédente : read(1) permet de lire un octet et read(1)[0] permet d’accéder à la valeur de l’octet lu. Attention
cette méthode lève une exception de type IndexError lorsque aucun octet n’a été lu (à la fin du fichier).
Lorsque la fin du fichier est atteinte, votre fonction renverra None. Vous pourrez vous inspirer du squelette
suivant :
try :
byte = stream . read (1)[0]
# A COMPLETER
except IndexError :
stream . close ()
return None
Question 30 Pour tester votre fonction, voici deux procédures qui s’occupent d’ouvrir les fichiers et écrire
les octets produits par votre fonction dans le fichier cible.
7
def convert_file ( source , dest , conversion ):
’’’
Convert ‘ source ‘ file using the ‘ conversion ‘ function and writes the
output in the ‘ dest ‘ file .
: param source : The name of the source file
: type source : str
: param dest : The name of the destination file
: type dest : str
: param conversion : A function which takes in parameter a stream ( opened \
in read and binary modes ) and which returns a tuple of bytes .
: type conversion : function
’’’
entree = open ( source , ’ rb ’)
sortie = open ( dest , ’ wb ’)
octets_sortie = conversion ( entree )
while octets_sortie != None :
sortie . write ( bytes ( octets_sortie ))
octets_sortie = conversion ( entree )
sortie . close ()
def c o n v e r t _ f i l e _ i s o l a t i n _ u t f 8 ( source , dest ):
’’’
Converts ‘ source ‘ file from ISO -8859 -1 encoding to UTF -8.
The output is written in the ‘ dest ‘ file .
’’’
convert_file ( source , dest , isolatin_to_utf8 )
Testez votre fonction sur le fichier cigale-ISO-8859-1.txt, en lançant la procédure convert_file_isolatin_utf8.
La procédure prend deux paramètres le nom du fichier source (le fichier au format ISO-8859-1) et le nom
du fichier cible (le nom du fichier à créer au format UTF-8) Vérifiez que vous obtenez le fichier attendu..
5.3
Conversion d’un fichier du format UTF-8 au format ISO-8859-1
La conversion d’un fichier du format UTF-8 au format ISO-8859-1 n’est pas toujours possible.
Nous ne considérerons donc pour ce TP que les fichiers pour lesquels cette conversion est possible.
Question 31 Écrivez en Python la fonction utf8_to_isolatin qui prend en paramètre un flux et qui
renvoie un tuple composé d’un seul octet, celui correspondant au caractère ISO-8859-1 équivalent du premier
caractère UTF-8 lu dans le flux.
Question 32 Testez votre fonction, via la procédure convert_file_utf8_isolatin qui est donnée ci-après,
sur le fichier cigale-UTF-8.txt.
def c o n v e r s i o n _ f i l e _ u t f 8 _ i s o l a t i n ( source , dest ):
’’’
Converts ‘ source ‘ file from UTF -8 encoding to ISO -8859 -1.
8
The output is written in the ‘ dest ‘ file .
’’’
convert_file ( source , dest , utf8_to_isolatin )
9

Documents pareils