Aller au contenu

E07 - ROT13⚓︎

Le problème

ROT13

Proposition 1⚓︎

🐍 Script Python
def ROT13(nb_carac, texte, texte2):

    for lettre in texte:
        if 65 < ord(lettre) < 78 :
            texte2 += chr(ord(lettre) + 13)
        elif 77 < ord(lettre) < 91 :
            texte2 += chr(ord(lettre) - 13)
        elif 96 < ord(lettre) < 110 :
            texte2 += chr(ord(lettre) + 13)
        elif 109 < ord(lettre) < 123 :
            texte2 += chr(ord(lettre) - 13)
        else : 
            texte2 += lettre
    return texte2








nb_carac = int(input())
texte = input()
texte2 = ""
print(ROT13(nb_carac, texte, texte2))
  • Très mauvais code.

  • texte2 ne devrait pas être passé en paramètre, mais initialisé dans la fonction.

  • On préfère une liste pour faire des ajouts successifs ; quitte à tout coller à la fin en str.

  • Tous ces nombres ne veulent rien dire à qui ne sait pas !!! Il ne faut pas coder ainsi !!!

Proposition 2⚓︎

🐍 Script Python
def ROT13(nb_caractère, chaine_caractère):
    """remplace les caractère de la chaine par des autre à 13 caractères de là.
    puis renvoie la chaine de caractère.
    Tous les caractères qui ne sont pas des lettres non accentuées de
    l'alphabet sont laissés tels quels.

    >>> ROT13(15, "Vive Prologin !")
    'Ivir Cebybtva !'

    """
    nouvelle_chaine_caractère = ""

    for i in range(nb_caractère):
        caractère = ord(chaine_caractère[i])
        if caractère >= 65 and caractère <= 90:
            if caractère > 77:
                caractère -= 13
            else:
                caractère += 13
        elif caractère >= 97 and caractère <= 122:
            if caractère > 109:
                caractère -= 13
            else:
                caractère += 13
        nouvelle_chaine_caractère += chr(caractère)
    return nouvelle_chaine_caractère


# test

import doctest
doctest.testmod()

# entrée

nb_caractère = int(input())
chaine_caractère = input()

# sortie

print(ROT13(nb_caractère, chaine_caractère))
  • C'est mal d'utiliser des nombres sans explication : 65, 77, 99 ???
  • C'est mal d'ajouter souvent des caractères à une chaine. Il vaut mieux ajouter à une liste, et faire un collage à la fin avec "".join(la_liste)
  • Bon doctest.

Proposition 3⚓︎

🐍 Script Python
def ROT13(liste_chaine: list, chaine_chiffrée: list, compteur: int) -> list:
    """
    Renvoie la chaine chiffrée, où les caractères alphabétique [A-Za-z] sont chiffrés avec ROT13 et tous les autres caractères sont inchangés.

    >>> ROT13(["!", " ", "n", "i", "g", "o", "l", "o", "r", "P", " ", "e", "v", "i", "V"], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0)
    ['I', 'v', 'i', 'r', ' ', 'C', 'e', 'b', 'y', 'b', 't', 'v', 'a', ' ', '!']

    """

    Alphabet_maj = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
    Alphabet_min = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"

    if liste_chaine == []:
        return chaine_chiffrée
    else:
        if liste_chaine[-1] in Alphabet_maj:
            chaine_chiffrée[compteur] = Alphabet_maj[ Alphabet_maj.index( liste_chaine[-1] ) + 13 ] 

        elif liste_chaine[-1] in Alphabet_min:
            chaine_chiffrée[compteur] = Alphabet_min[ Alphabet_min.index( liste_chaine[-1] ) + 13 ] 

        else:
            chaine_chiffrée[compteur] = liste_chaine[-1]
        liste_chaine.pop()
        return ROT13(liste_chaine, chaine_chiffrée, compteur + 1)
  • C'est très vilain d'utiliser des chaines de lettres pour l'alphabet ; ce n'est pas la méthode vue en classe !!!

  • Il manque un modulo 26, cela t'aurait évité d'écrire deux fois tes chaines de caractères. Dans le même esprit, tu aurais pu donner une chaine d'un seul alphabet ayant subi la rotation.

Proposition 4⚓︎

🐍 Script Python
import doctest


def chiffre(texte: str) -> str:
    """
    Renvoie le texte en ajoutant à l'indice de chaque lettres 13

    >>> chiffre('abcd')
    'nopq'
    """
    message_chiffré = ""
    for lettre in texte:
        message_chiffré += chiffrage_lettre(lettre)
    return message_chiffré


def chiffrage_lettre(lettre: str) -> str:
    """
    Renvoie la lettre à l'indice de la lettre + la constante CONT_rot_13

    >>> chiffrage_lettre('a')
    'n'
    """
    CONST_rot_13 = 13
    if 'a' <= lettre <= 'z':
        indice = ord(lettre) - ord('a') + CONST_rot_13
        return chr(ord('a') + indice % 26)
    elif 'A' <= lettre <= 'Z':
        indice = ord(lettre) - ord('A') + CONST_rot_13
        return chr(ord('A') + indice % 26)
    else:
        return lettre

import doctest
doctest.testmod()

# Entrée
nb_caractere_texte = int(input())
texte = input()

# Sorti
print(chiffre(texte))
  • Presque parfait.

  • inutile d'importer deux fois doctest, une fois suffit.

  • C'est mieux de définir la fonction chiffre après l'autre, et pas avant.

  • C'est mieux d'accumuler dans une liste, pour coller ensuite avec "".join(...). Avec une chaine, le hash est recalculé à chaque ajout ; lent.

Proposition 5⚓︎

🐍 Script Python
def ROT_treize(nb_caractères, text):
    """
    Affiche le text codée
    """
    for i in range(nb_caractères):
        if ord('A') <= ord(text[i]) <= ord('Z'):
            decode = chr(ord(text[i]) - ord('A') + 13)
            if ord(decode) > 25:
                decode = chr(ord(decode) - 26)
            decode = chr(ord(decode) + ord('A'))
            print(decode, end='')
        elif ord('a') <= ord(text[i]) <= ord('z'):
            decode =  chr(ord(text[i]) - ord('a') + 13)
            if ord(decode) > 25:
                decode = chr(ord(decode) - 26)
            decode = chr(ord(decode)+ ord('a'))
            print(decode,end='')
        else:
            print(text[i], end='')


#Entrée
nb_caractères = int(input())
text = input()

#Sortie
ROT_treize(nb_caractères, text)
  • Tu peux utiliser ROT13 comme identifiant, il faut juste ne pas commencer par un chiffre.

  • Il serait mieux de découper ta grande fonction avec une ou plusieurs autres fonctions auxiliaires.

Proposition 6⚓︎

🐍 Script Python
def ROT13(chaine: str, DECALAGE: int) -> list:
    """
    Fonction renvoyant une liste contenant pour chaque indice un caratère décalé de DECALAGE.
    >>> ROT13("Vive Prologin !", 13)
    ['I', 'v', 'i', 'r', ' ', 'C', 'e', 'b', 'y', 'b', 't', 'v', 'a', ' ', '!']

    """
    liste_finale = [0 for _ in range(len(chaine))]

    for i in range(len(chaine)):

        # Cas de lettre minuscule

        if ord('a') <= ord(chaine[i]) and ord(chaine[i]) <= ord('z'):
            liste_finale[i] = chr((ord(chaine[i]) + DECALAGE - ord('a')) % 26 + ord('a'))

        # Cas de lettre majuscule

        elif ord('A') <= ord(chaine[i]) and ord(chaine[i]) <= ord('Z'):
            liste_finale[i] = chr((ord(chaine[i]) + DECALAGE - ord('A')) % 26 + ord('A'))

        # Cas autre caratère

        else:
            liste_finale[i] = chaine[i]

    return liste_finale

# Entrées

nb_c = int(input())
chaine = input()
DECALAGE = 13

# Sortie

ans = ROT13(chaine, DECALAGE)
for k in range(len(ans)):
    print(ans[k], end='')

# Test

import doctest
doctest.testmod()
  • Il aurait été bon de faire le collage avant le return, pour renvoyer une str et non une list.

  • Ce n'est pas très propre d'initialiser la liste avec des entiers, pour y mettre progressivement des caractères ; cela fait un mélange interdit avec d'autres langages.

Corrigé du professeur⚓︎

🐍 Script Python
"""
author: Franck CHAMBON
problem: https://prologin.org/train/2004/semifinal/rot13
"""

def _rot13(caractere: str) -> str:
    """Renvoie le caractère avec une rotation de 13 dans l'alphabet,
    seulement si c'est une lettre, sinon inchangé.

    >>> _rot13('c')
    'p'
    >>> _rot13('E')
    'R'
    >>> _rot13('!')
    '!'

    """
    if len(caractere) != 1:
        raise ValueError(f"{len(caractere)} : mauvais nombre de caractères")
    elif 'a' <= caractere <= 'z':
        i = ord(caractere) - ord('a')
        i += 13
        if i >= 26:
            i-= 26
        return chr(ord('a') + i)
    elif 'A' <= caractere <= 'Z':
        i = ord(caractere) - ord('A')
        i += 13
        if i >= 26:
            i-= 26
        return chr(ord('A') + i)
    else:
        return caractere

def ROT13(texte: str) -> str:
    """Renvoie le texte chiffré avec la méthode ROT13.

    >>> ROT13("Vive Prologin !")
    'Ivir Cebybtva !'

    """
    return "".join(map(_rot13, texte))

# Tests
import doctest
doctest.testmod()

# Entrée
longueur = int(input())
texte = input()
assert longueur == len(texte)

# Sortie
print(ROT13(texte))

map

La fonction map permet d'appliquer la fonction _rot13 à tous les éléments de texte. On colle ensuite le résultat produit.

Il s'agit d'un style fonctionnel.

Fonction interne

Pour indiquer qu'une fonction est à usage interne, on peut la préfixer avec _. On l'utilise dans son module, on ne conseille pas son utilisation en dehors, sans l'interdire.

Variante

🐍 Script Python
i += 13
if i >= 26:
    i-= 26

pourrait être remplacé par

🐍 Script Python
if i >= 13:
    i -= 13
else:
    i += 13