TP : Codages et décodages 1 Le module coding

Transcription

TP : Codages et décodages 1 Le module coding
Univ. Lille 1 - Licence Informatique 2ème année
2015-2016
Codage de l’information
TP : Codages et décodages
Objectifs du TP
Ce TP a pour but
1. de coder et décoder avec différents types de codage.
Outils utilisés
— le langage Python.
Matériel fourni
— le module coding dont vous trouverez ici les fichiers sources et la documentation.
1
Le module coding
1.1
Présentation du module
Le module coding définit
1. un type Coding permettant la représentation d’un codage dont les symboles de l’alphabet
source sont des chaînes de caractères. Les mots du code associé sont des chaînes pouvant
contenir n’importe quels caractères (string).
2. une fonction de création de codage à partir d’un alphabet source représenté comme une liste
et d’un code lui aussi représenté par une liste.
3. une méthode (de la classe Coding) pour coder les symboles
Elle déclenche l’exception Not_coding_symbol si le symbole donné n’est pas dans l’alphabet
source.
4. une fonction pour décoder les mots
Elle déclenche l’exception Undecodable_word si le mot donné n’est pas dans le code associé
au codage.
Pour une documentation complète voir ici.
1.2
Installation et utilisation du module
Question 1
1. Récupérez les fichiers sources de ce module :
— coding.zip.
2. Décompressez cette archive dans votre répertoire de travail. Vous devez obtenir un fichier coding.py
et un dossier Doc contenant la documentation au format HTML.
3. À l’aide d’un navigateur (Firefox par exemple), lisez la documentation sur ce module.
4. Lancer un interpréteur Python
1
> python3
5. Puis au niveau de l’interpréteur tapez la directive
from coding import *
afin de ne pas devoir préfixer les noms définis par le module par son nom.
1.3
Premier essai du module
Vous allez maintenant faire votre premier essai du module avec un petit codage dont l’alphabet
source contient trois symboles montré à la table 1.
a
b
c
010
100
110
Table 1 – Exemple de codage
On définit d’abord l’alphabet source comme un tableau de trois caractères,
source_alphabet = [ ’a ’ , ’b ’ , ’c ’]
puis le code comme un tableau de trois chaînes de caractères contenant les trois mots du code (dans
le même ordre que celui utilisé pour l’ordre des symboles de l’alphabet source),
code = [ ’ 010 ’ , ’ 100 ’ , ’ 110 ’]
pour enfin définir le codage.
my_coding = create ( source_alphabet , code )
Ceci fait, nous sommes en mesure de coder des symboles et décoder des mots.
Question 2 À l’aide de la méthode code, codez successivement chacun des trois symboles de l’alphabet
source.
Question 3 À l’aide de la méthode decode, décodez successivement chacun des trois mots du code.
Question 4 Que se passe-t-il si vous tentez de coder un symbole qui n’est pas dans l’alphabet source ?
Question 5 Et si vous tentez de décoder un mot qui n’est pas dans le code ?
2
Les codages utilisés dans le TP
Dans ce TP vous allez utiliser trois codages :
2
1. un codage binaire de longueur fixe (cf table 2) ;
2. un codage à virgule (cf table 3). C’est le codage Morse dans lequel le silence espaçant les codes
est symbolisé par /, et auquel on a ajouté un code fictif pour l’espace ;
3. et un codage binaire préfixe (cf table 4).
Ces trois codages codent les symbole d’un même alphabet source S constitué des 26 lettres latines
plus l’espace (ESP).
A
I
Q
Y
00000
01000
10000
11000
B
J
R
Z
00001
01001
10001
11001
C
K
S
ESP
00010
01010
10010
11111
D
L
T
00011
01011
10011
E
M
U
00100
01100
10100
F
N
V
00101
01101
10101
G
O
W
00110
01110
10110
H
P
X
00111
01111
10111
Table 2 – Codage de longueur fixe
A
I
Q
Y
.-/
../
--.-/
-.--/
B
J
R
Z
-.../
.---/
.-./
--../
C
K
S
ESP
-.-./
-.-/
.../
---./
D
L
T
-../
.-../
-/
E
M
U
./
--/
..-/
F
N
V
..-./
-./
...-/
G
O
W
--./
---/
.--/
H
P
X
..../
.--./
-..-/
Table 3 – Codage à virgule (morse + code fictif pour l’espace)
A
G
M
S
Y
1010
0111110
00101
1011
0111111111
B
H
N
T
Z
0010011
0010010
1001
0110
01111111101
C
I
O
U
ESP
01001
1000
0000
0011
111
D
J
P
V
01110
011111110
01000
001000
E
K
Q
W
110
011111111001
0111101
011111111000
F
L
R
X
0111100
0001
0101
01111110
Table 4 – Codage préfixe
2.1
L’alphabet source
L’alphabet source est représenté par un tableau de caractères (char array).
source_alphabet = \
[ ’A ’ , ’B ’ , ’C ’ , ’D ’ , ’E ’ , ’F ’ , ’G ’ , ’H ’ , ’I ’ , ’J ’ , ’K ’ , ’L ’ , ’M ’ , ’N ’ ,\
’O ’ , ’P ’ , ’Q ’ , ’R ’ , ’S ’ , ’T ’ , ’U ’ , ’V ’ , ’W ’ , ’X ’ , ’Y ’ , ’Z ’ , ’ ’]
2.2
Les codes
Les codes associés à ces codages sont des tableaux de chaînes de caractères (string array).
code1 = \
[ " 00000 " , " 00001 " , " 00010 " , " 00011 " , " 00100 " , " 00101 " , " 00110 " , " 00111 " ,
3
" 01000 " , " 01001 " , " 01010 " , " 01011 " , " 01100 " , " 01101 " , " 01110 " , " 01111 " ,
" 10000 " , " 10001 " , " 10010 " , " 10011 " , " 10100 " , " 10101 " , " 10110 " , " 10111 " ,
" 11000 " , " 11001 " , " 11111 " ]
code2 = \
[ " . -/ " , " -.../ " , " -. -./ " , " -../ " , " ./ " , " .. -./ " , " - -./ " , " ..../ " , " ../ " ,
" . - - -/ " , " -. -/ " , " . -../ " , " - -/ " , " -./ " , " - - -/ " , " . - -./ " , " - -. -/ " , " . -./ " ,
" .../ " , " -/ " , " .. -/ " , " ... -/ " , " . - -/ " , " -.. -/ " , " -. - -/ " , " - -../ " , " - - -./ " ]
code3 = \
[ " 1010 " , " 0010011 " , " 01001 " , " 01110 " , " 110 " , " 0111100 " , " 0111110 " ,
" 0010010 " , " 1000 " , " 011111110 " , " 011111111001 " , " 0001 " , " 00101 " ,
" 1001 " , " 0000 " , " 01000 " , " 0111101 " , " 0101 " , " 1011 " , " 0110 " , " 0011 " ,
" 001000 " , " 011111111000 " , " 01111110 " , " 0111111111 " , " 01111111101 " ,
" 111 " ]
2.3
Création des codages
Question 6 À l’aide de la fonction create du module coding, déclarez trois variables nommées coding1,
coding2 et coding3 pour chacun de ces trois codages.
Question 7 Testez ces codages pour coder des caractères et décoder des mots du code.
3
La fonction de codage
La méthode code de la classe Coding code des symboles mais pas les mots construits sur l’alphabet source.
Il s’agit de réaliser une fonction de codage des mots que vous appellerez code_word, qui prend
en paramètre le mot à coder ainsi que le codage (de type Coding) à effectuer et qui renvoie une
chaîne de caractère encodé avec le codage passé en paramètre.
Question 8 Réalisez cette fonction. Vous pouvez envisager un algorithme récursif ou itératif. L’algorithme
récursif étant certainement le plus simple à mettre en œuvre.
Question 9 Codez le mot CODAGE avec chacun des trois codages définis précédemment. Vous devez obtenir
— 000100111000011000000011000100 pour le codage 1 ;
— -.-./---/-../.-/--././ pour le codage 2 ;
— et 0100100000111010100111110110 pour le codage 3.
4
Le décodage pour les codages de longueur fixe
Vous allez programmer le décodage pour des mots codés avec un codage de longueur fixe. Ce
décodage ne s’appliquera donc que sur le premier des trois exemples de codage de ce TP.
4
Question 10 Étant donné un codage que l’on suppose être de longueur fixe, et un symbole de l’alphabet
source, quelle expression en Python permet de connaître la longueur commune des mots du code associé.
Vérifiez votre réponse avec le coding1.
Vous appellerez decode_fixed_length_word la fonction de décodage que vous allez programmer
et qui aura pour paramètres
1. une chaîne de caractères représentant le mot à décoder :
2. un Coding.
Cette fonction renverra le mot décodé.
Question 11 Réalisez cette fonction. Elle doit déclencher l’exception
coding.Undecodable_word "decode_fixed_length_word: undecodable word"
si le mot à décoder n’est pas décodable.
Question 12 Testez votre fonction avec le mot obtenu en codant le mot CODAGE avec le codage 1.
Question 13 Décodez le mot suivant qui a été obtenu avec le coding1.
01011000001111101111001110100001\
01100000011011001100111100010111\
00111101000001001111100011001001\
11110101111111011101010010101100\
01010000010010001111110001000111\
00000100010111100100011011001101\
0000010010001
(Copiez/collez ce mot et ajoutez les guillemets nécessaires.)
5
Le décodage pour les codages à virgule
Vous allez programmer le décodage pour des mots codés avec un codage à virgule (dont la virgule
se trouve à la fin). Ce décodage ne s’appliquera donc que sur le deuxième des trois exemples de
codage de ce TP.
Une méthode qui pourra s’avérer bien utile pour le décodage est celle prédéfinie pour les chaînes
de caractères str.find. Cette méthode permet de rechercher une chaîne dans une autre chaîne.
Elle renvoie la position de la première occurrence dans la chaîne ou −1 en cas d’échec.
Question 14 Utiliser cette fonction sur de nombreux exemples renvoyant un résultat ou déclenchant une
exception.
Vous appellerez decode_comma_word la fonction de décodage que vous allez programmer et qui
devra avoir pour paramètres :
1. le mot à décoder ;
2. le caractère servant de virgule :
5
3. le codage à appliquer.
La fonction renverra le résultat du décodage.
Question 15 Réalisez cette fonction. Elle doit déclencher l’exception
coding.Undecodable_word: "decode_comma_word: comma not found, cannot decode the word"
si le mot à décoder n’est pas décodable.
Question 16 Décodez le mot suivant qui a été obtenu avec le codage 2.
.--./---/..-/.-./---./.-../.-/--\
-./..-./.-./.-/-./-.-././---./-.\
./---././-./---./-.../.-/.../---\
./-.././.../---./-./---/..-/../.\
-../.-.././.../---././-./-.-./--\
-/.-././
(Copiez/collez ce mot et ajoutez les guillemets (") nécessaires.)
6
Le décodage pour les codages préfixes
Vous allez enfin programmer le décodage pour des mots codés avec un codage préfixe. Ce décodage pourra donc s’appliquer aux trois exemples de codage de ce TP.
La méthode de décodage employée pour ce TP consiste à essayer tous les préfixes non vides
du mot à décoder jusqu’à en trouver un qui soit décodable avec la fonction decode, ou jusqu’à
épuisement de tous les préfixes auquel cas le mot n’est pas décodable 1 .
Voici le code en Python d’une fonction nommée decode_prefix_letter qui prend en paramètre le mot à décoder ainsi que le codage à utiliser et qui renvoie un tuple composé du premier
caractère décodé et de l’indice du reste du mot à décoder. La fonction déclenche l’exception
Mot_non_decodable "decode_lettre_prefixe : aucun prefixe decodable"
si le mot ne possède aucun préfixe décodable.
def d e c o d e _ p r e f ix _ l e t t e r ( word , my_coding ):
’’’
Decodes the first letter of the word , assuming a prefix coding was used .
: param word : A word that was coded using ‘ coding ‘
: type word : str
: param my_coding : The coding used for ( de ) coding
: type my_coding : coding . Coding
: return : a tuple whose elements are : 1) the symbol associated with the \
first decodable prefix 2) the length of the first decodable prefix
: rtype : tuple
: CU : ‘ word ‘ was coded using ‘ my_coding ‘
1. Cette méthode n’est pas la plus efficace. Il est préférable d’utiliser une représentation arborescente du codage,
comme il a été vu en cours. Cela fera l’objet d’un prochain TP.
6
: Examples :
>>> d e c o d e_ p r e f i x _ l e t t e r ("0010010" , coding3 )
( ’ H ’, 7)
>>> d e c o d e_ p r e f i x _ l e t t e r ("00100101000" , coding3 )
( ’ H ’, 7)
>>> d e c o d e_ p r e f i x _ l e t t e r ("00" , coding3 )
Traceback ( most recent call last ):
...
coding . Undecodable_word : d e c o d e _ pr e f i x _ l e t t e r : no decodable prefix
’’’
word_length = len ( word )
for i in range (1 , word_length +1):
try :
prefix = my_coding . decode ( word [: i ])
return ( prefix , i )
except :
pass
raise Undecodable_word
Question 17 Utilisez cette fonction pour répondre à la question précédente.
Vous appellerez decode_prefix_word la fonction de décodage que vous allez programmer et qui
devra avoir pou paramètres :
1. le mot à décoder
2. le codage à appliquer.
La fonction renverra le mot décodé.
Question 18 Réalisez cette fonction. Elle doit déclencher l’exception
coding.Undecodable_word
si le mot à décoder n’est pas décodable.
Question 19 Décodez le mot suivant qui a été obtenu avec le coding3.
01100010010101000011101011111110\
10110110111011000000011011111110\
00000011010110111111010111011110\
0101010000101110
(Copiez/collez ce mot et ajoutez les guillemets (") nécessaires.)
7
Stockage et lecture en binaire
Afin de stocker des mots binaires il faut être capable d’écrire en binaire dans un fichier. Or il est
possible d’écrire au minimum un octet à la fois dans un fichier. Il est donc nécessaire de programmer
7
des fonctions qui, à partir d’une chaîne binaire, vont convertir les bits en octets et écrire ces octets
dans un fichier.
Pour ce faire nous allons avoir besoin des fonctions binary_to_bytes et byte_to_binary réalisées au TP 2. Importez-les dans votre fichier (attention « importer » ne veut pas dire copier-coller).
Question 20 Réalisez une fonction write_bits (dont la documentation est donnée).
Rappelons qu’un flux est (potentiellement) un fichier déjà ouvert. La fonction write_bits n’a donc pas
à faire appel à la fonction open. Pour savoir si le flux a été ouvert en écriture et en binaire, on peut utiliser
l’attribut mode du flux (et vérifier si un b s’y trouve) ainsi que la méthode writable.
Pour se familiariser avec les flux ouverts en mode binaire, tapez les instructions suivantes dans un
interpréteur Python :
>>> f = open ( ’ file . txt ’ , ’ wb ’)
>>> f . write ( bytes ([65 , 66]))
>>> f . close ()
Quel est le contenu du fichier file.txt ? Inspirez-vous ensuite de l’exemple pour réaliser la fonction.
Question 21 La fonction write_bits n’écrira dans le fichier de sortie que s’il y a au moins 8 bits à écrire.
Cela pose évidemment problème si la chaîne à écrire n’a pas un nombre de bits qui est un multiple de 8.
Pour remédier à cela, nous allons faire une fonction complete_byte (voir la documentation) qui prend
en paramètre une chaîne binaire de longueur strictement inférieure à 8 et qui la complète avec un 1 suivi
de suffisamment de 0 de façon à ce que la chaîne résultante soit de longueur 8.
Question 22 Réalisez une fonction read_bits (voir la documentation)
Question 23 Réalisez la fonction uncomplete_byte (voir la documentation), inverse de la fonction
complete_byte, qui prend en paramètre une chaîne binaire de 8 caractères et qui renvoie la chaîne en
retirant les bits ajoutés pour la complétion.
Pour réaliser cette fonction on remarquera que le code utilisé pour la complétion est un code à virgule,
où la virgule est à gauche (il s’agit du 1). À ce titre la méthode rfind appliquée sur les chaînes de caractères
sera utile pour trouver la position du dernier 1.
Question 24 Réalisez une fonction remove_completion (voir la documentation) qui prend une chaîne
de caractères binaires représentant au moins 8 bits et qui retire la complétion qui lui a été ajoutée. Cette
fonction utilisera évidemment la fonction précédente.
Ces fonctions permettent maintenant d’écrire et lire facilement en binaire dans des fichiers. À
titre d’exemple, une procédure d’écriture et une fonction de lecture sont fournies. Elles reposent sur
les fonctions écrites précédemment.
def fl u s h_ b in a r y_ s tr i ng ( binary , stream ):
’’’
Flush a binary string by writing as many bytes as possible in the output
stream .
8
: param binary : A binary string
: type binary : str
: param stream : An output stream
: return : the bits that could not be written in the output stream ( the \
length of the returned string is necessarily < 8).
: Examples :
>>> import tempfile ; r = tempfile . Na med Te mp ora ry Fi le ()
>>> fl u sh _ bi n a ry _ st r i ng ( ’01000001 ’ , r )
’’
>>> r . seek (0);
0
>>> r . read (). decode ()
’A ’
’’’
while len ( binary ) >= 8:
binary = write_bits ( stream , binary )
return binary
def w r i t e _ b i n a r y _ s t r i n g _ i n _ f i l e ( binary , file ):
’’’
Write the binary string in the file ( the string is written 8 bits per 8
bits in the file ).
As the binary string can have any length , the last byte will be completed
so that all the content could be written to the file .
: param binary : a binary string
: type binary : str
: param file : The filename of the file where the binary string will be \
written
: type file : str
: Examples :
>>> import tempfile ; r = tempfile . Na med Te mp ora ry Fi le ()
>>> w r i t e _ b i n a r y _ s t r i n g _ i n _ f i l e ( ’01000001010 ’ , r . name )
>>> r . seek (0);
0
>>> r . read (). decode ()
’ AP ’
’’’
out_file = open ( file , ’ wb ’)
binary = f l us h _b i n ar y _s t ri n g ( binary , out_file )
write_bits ( out_file , complete_byte ( binary ))
out_file . close ()
def read_file ( file ):
9
’’’
Read the data in the file and returns a binary string corresponding to
that data .
: param file : the filanem of the file to read .
: type file : str
: return : The binary string of the data that was stored in the file . The \
completion will be removed from the binary string .
: rtype : str
: Examples :
>>> import tempfile ; r = tempfile . Na med Te mp ora ry Fi le ()
>>> w r i t e _ b i n a r y _ s t r i n g _ i n _ f i l e ( ’01000001010 ’ , r . name )
>>> r . seek (0);
0
>>> read_file ( r . name )
’01000001010 ’
’’’
in_file = open ( file , ’ rb ’)
bits = ’ ’
binaire = read_bits ( in_file )
while binaire != ’ ’:
bits += binaire
binaire = read_bits ( in_file )
in_file . close
if len ( bits ) > 0:
bits = remove_c ompletio n ( bits )
return bits
Question 25 Stockez la chaîne binaire de la question 19, dans un fichier appelé mot3.data en utilisant
write_binary_string_in_file. Quelle est la taille du fichier mot3.data ? Quelle est la longueur de la chaîne
mot3 ? Comment expliquez-vous la différence ?
Question 26 À l’aide de la fonction read_file, lisez le contenu du fichier mot3.data et vérifiez que la
chaîne obtenue est identique à mot3.
10