Épreuve pratique · 2026 · Sujet 22

Bac NSI pratique 2026 sujet 22 : Code matriciel simplifié, binaire et table ASCII

Le « QR code simplifié » du sujet est une grille de 6×8 cases : noir vaut 1, blanc vaut 0, chaque ligne encode un entier puis un caractère ASCII. Il ne s’agit pas du format normalisé d’un QR code réel : aucun repère de position ni code correcteur n’est implémenté ici. Le but est de comprendre la chaîne bits → entier → caractère et son inverse. ASCII associe les codes 0 à 127 ; par exemple 01100001 vaut 97 et correspond àa.

Dans ce sujet

Un corrigé pédagogique pour comprendre et justifier vos réponses. Les conseils de rédaction ne constituent pas un barème officiel détaillé.

Application

Code matriciel simplifié, binaire et table ASCII

Le « QR code simplifié » du sujet est une grille de 6×8 cases : noir vaut 1, blanc vaut 0, chaque ligne encode un entier puis un caractère ASCII. Il ne s’agit pas du format normalisé d’un QR code réel : aucun repère de position ni code correcteur n’est implémenté ici. Le but est de comprendre la chaîne bits → entier → caractère et son inverse. ASCII associe les codes 0 à 127 ; par exemple 01100001 vaut 97 et correspond àa.

010011010010111001001000011000010111001001100001
Figure 1 reconstruite : chaque ligne encode un caractère sur 8 bits.
LigneTuple
1(0, 1, 0, 0, 1, 1, 0, 1)
2(0, 0, 1, 0, 1, 1, 1, 0)
3(0, 1, 0, 0, 1, 0, 0, 0)
4(0, 1, 1, 0, 0, 0, 0, 1)
5(0, 1, 1, 1, 0, 0, 1, 0)
6(0, 1, 1, 0, 0, 0, 0, 1)
Figure 2 : table ASCII complète, codes 0 à 127
Codes 0..7 du bloc+1+2+3+4+5+6+7
0: NUL1: SOH2: STX3: ETX4: EOT5: ENQ6: ACK7: BEL
8: BS9: HT10: LF11: VT12: FF13: CR14: SO15: SI
16: DLE17: DC118: DC219: DC320: DC421: NAK22: SYN23: ETB
24: CAN25: EM26: SUB27: ESC28: FS29: GS30: RS31: US
32: espace33: !34: "35: #36: $37: %38: &39: '
40: (41: )42: *43: +44: ,45: -46: .47: /
48: 049: 150: 251: 352: 453: 554: 655: 7
56: 857: 958: :59: ;60: <61: =62: >63: ?
64: @65: A66: B67: C68: D69: E70: F71: G
72: H73: I74: J75: K76: L77: M78: N79: O
80: P81: Q82: R83: S84: T85: U86: V87: W
88: X89: Y90: Z91: [92: \93: ]94: ^95: _
96: `97: a98: b99: c100: d101: e102: f103: g
104: h105: i106: j107: k108: l109: m110: n111: o
112: p113: q114: r115: s116: t117: u118: v119: w
120: x121: y122: z123: {124: |125: }126: ~127: DEL

Les noms NUL, SOH… etDEL désignent les caractères de contrôle. Le module fourni les représente par ces noms lisibles plutôt que par les caractères de contrôle eux-mêmes.

Le dossier comporte le sujet et les fichiers listés dans les téléchargements. Les codes de départ sont reproduits ci-dessous. Travaillez sur une copie. Les appels professeur signalés dans les questions servent à présenter votre démarche ; vous pouvez aussi demander de l’aide en cas de difficulté.

Code fourni : ascii.py
figure1 = [(0, 1, 0, 0, 1, 1, 0, 1),
           (0, 0, 1, 0, 1, 1, 1, 0),
           (0, 1, 0, 0, 1, 0, 0, 0),
           (0, 1, 1, 0, 0, 0, 0, 1),
           (0, 1, 1, 1, 0, 0, 1, 0),
           (0, 1, 1, 0, 0, 0, 0, 1)]

dict_ascii = {
    0: "NUL", 1: "SOH", 2: "STX", 3: "ETX", 4: "EOT", 5: "ENQ", 6: "ACK", 7: "BEL",
    8: "BS", 9: "HT", 10: "LF", 11: "VT", 12: "FF", 13: "CR", 14: "SO", 15: "SI",
    16: "DLE", 17: "DC1", 18: "DC2", 19: "DC3", 20: "DC4", 21: "NAK", 22: "SYN", 23: "ETB",
    24: "CAN", 25: "EM", 26: "SUB", 27: "ESC", 28: "FS", 29: "GS", 30: "RS", 31: "US",
    32: " ", 33: "!", 34: "\"", 35: "#", 36: "$", 37: "%", 38: "&", 39: "'",
    40: "(", 41: ")", 42: "*", 43: "+", 44: ",", 45: "-", 46: ".", 47: "/",
    48: "0", 49: "1", 50: "2", 51: "3", 52: "4", 53: "5", 54: "6", 55: "7",
    56: "8", 57: "9", 58: ":", 59: ";", 60: "<", 61: "=", 62: ">", 63: "?",
    64: "@", 65: "A", 66: "B", 67: "C", 68: "D", 69: "E", 70: "F", 71: "G",
    72: "H", 73: "I", 74: "J", 75: "K", 76: "L", 77: "M", 78: "N", 79: "O",
    80: "P", 81: "Q", 82: "R", 83: "S", 84: "T", 85: "U", 86: "V", 87: "W",
    88: "X", 89: "Y", 90: "Z", 91: "[", 92: "\\", 93: "]", 94: "^", 95: "_",
    96: "`", 97: "a", 98: "b", 99: "c", 100: "d", 101: "e", 102: "f", 103: "g",
    104: "h", 105: "i", 106: "j", 107: "k", 108: "l", 109: "m", 110: "n", 111: "o",
    112: "p", 113: "q", 114: "r", 115: "s", 116: "t", 117: "u", 118: "v", 119: "w",
    120: "x", 121: "y", 122: "z", 123: "{", 124: "|", 125: "}", 126: "~", 127: "DEL"
}

test1 = [84, 101, 115, 116, 32, 49, 32, 114, 101, 117, 115, 115, 105, 33]

test2 = [84, 101, 115, 116, 32, 50, 32, 114, 101, 117, 115, 115, 105, 33]

test3 = [84, 101, 115, 116, 32, 51, 32, 114, 233, 117, 115, 115, 105, 33]
Code fourni : qrcode.py
import ascii

#############################################################################
# Question 1 et 2 : Écrire les codes des fonctions bin2dec et qrcode2dec
#              Proposer un test de qrcode2dec
#############################################################################


# implémentation du QR Code de la figure 1:
qrcode_fig1 = ascii.figure1


#############################################################################
# Question 3 : Fonctions dec2str et test_dec2str
#############################################################################
def dec2str(liste_dec):
    """ entrée: liste d'entiers décimaux
        sortie: chaine de caractère formée des caractères correspondant
        de la table ascii """
    table_ascii = ascii.dict_ascii
    chaine = ""
    for entier in liste_dec:
        chaine += table_ascii[entier]
    return chaine


def test_dec2str():
    """ Teste la fonction dec2str avec des données issues du module fourni """
    tests = [ascii.test1, ascii.test2, ascii.test3]
    for test in tests:
        print(dec2str(test))


def qrcode2str(qrcode):
    return dec2str(qrcode2dec(qrcode))

#############################################################################
# Question 4 : Fonction str2qrcode déficiente
#############################################################################


def str2qrcode(message):
    """
    Convertit une chaine de caractères en liste de tuples binaires.
    """
    qrcode = []
    table_inverse = {valeur: cle for cle, valeur in ascii.dict_ascii.items()}

    for caractere in message:
        entier = table_inverse.get(caractere, 63)
        binaire_str = bin(entier)[2:]
        ligne = tuple(int(bit) for bit in binaire_str)
        qrcode.append(ligne)

    return qrcode

Question 1

#

Écrire bin2dec, qui reçoit un tuple de bits et renvoie l’entier naturel correspondant. Décoder la figure 1 pour découvrir le nom contenu dans le message.

Indice

Lire le bit suivant revient à doubler la valeur déjà accumulée.

Comprendre la correction
def bin2dec(bits):
    valeur = 0
    for bit in bits:
        valeur = 2 * valeur + bit
    return valeur

En lisant de gauche à droite, multiplier par 2 décale la valeur d’une position binaire, puis ajouter le bit incorpore le chiffre courant. Pour 01100001, les valeurs successives sont 0,1,3,6,12,24,48,97. Les six lignes de la figure donnent 77,46,72,97,114,97, donc le message est M. Hara. Le zéro de tête ne change pas la valeur numérique, mais sera indispensable pour dessiner une ligne de largeur fixe à la question 4.

assert bin2dec((0,1,1,0,0,0,0,1)) == 97
assert bin2dec((0,0,0,0,0,0,0,0)) == 0
assert bin2dec((1,1,1,1,1,1,1,1)) == 255

La conversion par accumulation se justifie avec un invariant simple : après chaque bit, la variable contient la valeur du préfixe déjà lu. Si ce préfixe vaut v, ajouter un bit b à droite produit 2v + b, exactement comme écrire un chiffre supplémentaire en base 10 produit 10v + chiffre. On peut donc convertir des tuples de longueurs différentes avec le même algorithme. La contrainte de huit bits appartient au format graphique de ce sujet, pas à la règle générale de conversion.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 2

#

Écrire qrcode2dec, qui transforme une liste de tuples en liste d’entiers. Proposer un test utilisant la figure 1 du module ascii.py.

Comprendre la correction
def qrcode2dec(qrcode):
    return [bin2dec(ligne) for ligne in qrcode]
assert qrcode2dec(ascii.figure1) == [77,46,72,97,114,97]
assert qrcode2dec([]) == []

Chaque ligne produit exactement un entier et l’ordre des lignes est conservé. Réutiliser bin2dec évite de réécrire une conversion déjà testée. Ce test de toute la figure vérifie à la fois les valeurs et leur ordre, ce qu’un test limité au nombre de lignes ne ferait pas.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 3

#

Exécuter test_dec 2 str. Identifier le problème puis modifier dec2str afin de toujours renvoyer une chaîne lisible.

Indice

Comparer 233 à l’ensemble des clés de dict_ascii.

Comprendre la correction

Les deux premiers tests affichent Test 1 reussi! et Test 2 reussi! avec les espaces du jeu fourni. Le troisième contient 233, absent de la table 0..127 : l’accès par crochets provoque KeyError.233 est utilisé ici pour un caractère accentué dans une autre convention d’encodage, mais cela ne l’ajoute pas à ASCII. Nous adoptons un remplacement visible ? pour tout entier absent.

def dec2str(liste_dec):
    chaine = ''
    for entier in liste_dec:
        chaine += ascii.dict_ascii.get(entier, '?')
    return chaine
assert dec2str(ascii.test3) == 'Test 3 r?ussi!'
assert dec2str([65,233,66]) == 'A?B'
assert dec2str([]) == ''
test_dec2str()

Le remplacement conserve un signe visible à l’endroit non décodable et évite de masquer la perte. Supprimer la valeur inconnue raccourcirait silencieusement le message. Cette convention n’invente pas le caractère original : il faut connaître l’encodage source pour le retrouver exactement.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 4

#

str2qrcode ne reproduit pas exactement la figure 1 pour son message. Identifier l’origine et modifier le code pour obtenir des lignes valides de huit bits.

PythonEncoder un message sans perdre les zéros de têteÉcrivez votre solution et mettez-la à l’épreuve

Corrigez str2qrcode(message). Chaque caractère devient un tuple de huit bits à partir de la table ascii.dict_ascii fournie. Un caractère absent est remplacé par le code 63, celui de ?. Renvoyez les lignes dans l’ordre du message. Cet atelier reconstruit les données du dessin, sans bibliothèque graphique.

def str2qrcode(message):
    # À vous de jouer
    pass

Les cas de test proposés :

  • Figure 1 du sujet : L’égalité vérifie chaque bit et l’ordre des six lignes.
  • Le point demande deux zéros initiaux : 46 s’écrit 101110 en binaire minimal, mais le dessin demande huit cases.
  • Espace et zéro : Un espace est un caractère avec un code, pas une ligne à supprimer.
  • Caractère absent : La convention du sujet remplace le caractère inconnu par ?, sans inventer son encodage.
  • Tous les caractères ASCII imprimables : La reconstruction indépendante par les poids binaires vérifie les 95 caractères imprimables.
  • Message vide : Aucun caractère ne produit aucune ligne.
Indice

Comparer la longueur binaire de 46 et celle attendue pour une ligne.

Comprendre la correction

bin(entier)[2:] supprime le préfixe 0 b, mais ne complète pas les zéros de tête. Le point 46 est écrit 101110 sur 6 bits au lieu de 00101110 sur 8. Il faut imposer la largeur avant de créer le tuple.

def str2qrcode(message):
    table_inverse = {caractere: entier for entier, caractere in ascii.dict_ascii.items()}
    qrcode = []
    for caractere in message:
        entier = table_inverse.get(caractere, 63)
        binaire = bin(entier)[2:].zfill(8)
        qrcode.append(tuple(int(bit) for bit in binaire))
    return qrcode
assert str2qrcode('M.Hara') == ascii.figure1
assert all(len(ligne) == 8 for ligne in str2qrcode('M.Hara ?'))
assert str2qrcode('é') == [(0,0,1,1,1,1,1,1)]

La dernière assertion vérifie la convention déjà fournie : un caractère absent est remplacé par 63, le code de?. zfill(8) conserve la valeur tout en fixant le format graphique. Les valeurs de la table étant au plus 127, huit bits suffisent. Le dictionnaire représente les contrôles par des noms commeLF ; parcourir une chaîne LF traite deux lettres, pas un retour à la ligne. La réversibilité attendue concerne donc les caractères imprimables pris en charge.

Le test aller-retour doit distinguer deux propriétés. Pour un caractère ASCII imprimable reconnu, décoder ce que l’on vient d’encoder doit restituer ce caractère. Pour un caractère non pris en charge comme é, le programme fourni prévoit un remplacement par ?, donc cet aller-retour ne peut pas restituer l’original. Il faut le dire au lieu de promettre une conversion sans perte pour tout texte. Le test d’égalité avec la figure 1 contrôle en plus les zéros initiaux et le nombre exact de cases par ligne.

Valeur numérique et largeur graphiqueUn atelier pour expérimenter

Choisissez une réponse puis confrontez votre intuition à l’explication. Vous pouvez modifier vos choix.

Lire les réponses et leurs explications
  1. 101110 et 00101110 ont…

    La même valeur, des largeurs différentes.. Les zéros initiaux ne changent pas l’entier.

  2. Le code 233 est-il un code ASCII de cette table ?

    Non, les clés vont de 0 à 127.. La borne de la table fournie est 127.

Appuyez votre explication sur les valeurs et les conditions exactes du programme.

Voir la question dans le sujet PDF, p. 4 (nouvel onglet)
Assembler un octet, lire son caractèreUn atelier pour expérimenter

Changez les huit bits. Chaque position affiche son poids. La valeur décimale est convertie selon ASCII ; hors 0..127, le remplacement? est affiché.

Lire le résultat de l’expérience initiale

01001101 = 77 ; caractère : M.

Chaque bit1 apporte son poids. Le premier bit doit être0 pour rester dans la plage ASCII0..127.

PositionBitPoidsContribution
001280
116464
20320
30160
4188
5144
6020
7111

Une donnée combine une valeur, une largeur de représentation et une convention de décodage.

Revoir les notions de cet exercice

Du sujet à la méthode

Votre prochaine séance de révision

  • Tester séparément conversion numérique et format des lignes.
  • Un remplacement de caractère doit être explicite car il perd de l’information.

Retrouver ces notions dans d’autres sujets

Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027

Énoncé : sujet 26_BCG_NSI_22 (PDF). Corrigé et explications pédagogiques proposés par Sofien.