URL: https://linuxfr.org/users/guillaum/journaux/python-encodage-rot13-brainfuck Title: Python, encodage, rot13, brainfuck Authors: Guillaum Date: 2010年10月27日T16:10:12+02:00 Tags: python et utf8 Score: 45 Bonjour chers amis. Comme je n'ai pas (plus diraient certains) de blog, et que là je suis tellement content de ce que j'ai trouvé que j'ai envi d'en informer le monde, et bien voila, je m'adresse à toi, cher public (et non cher 'Nal). Vous connaissez tous [[Python_(langage)]] comme étant le meilleur langage du monde. Aussi vous devez savoir que les sources d'un bout de code python peuvent être écrite dans n'importe quel [[Codage_des_caractères]] (genre ascii, UTF-8, ...). Par exemple en faisant ceci : # coding: utf8 print "Ceci est du code python encodé en utf8" Jusqu'à présent je pensais qu'il s'agissait d'un comportement en dur dans l'interpréteur en fait il n'en est rien, c'est beaucoup plus malin que cela. Python définit un module [codecs](http://docs.python.org/library/codecs.html) pour gérer les différents codages de caractères, et c'est ce module qui est appelé par l'interpréteur pour traduire en unicode le code que l'on a écrit. Chaque encodage différent est représenté dans _/usr/lib/pythonX.Y/encodings_ par un fichier _name.py_ contenant les fonctions d'encodage et de décodage adaptées. Ainsi il est possible d'écrire son code source dans n'importe quel encodage supporté dans ce répertoire. Or il y en a un que j'utilise depuis longtemps pour protéger ma vie privé (joke inside), c'est l'encodage [[ROT13]]. Un petit exemple en mode interactif de python : >>> u'Hello linuxfr.org'.encode('rot13') 'Uryyb yvahkse.bet' >>> 'Uryyb yvahkse.bet'.decode('rot13') u'Hello linuxfr.org' Et là la question qui m'est venue ce matin c'est "Est il possible d'écrire un code source en rot13 ?" Essayons : Le code original: for i in range(5): £spaces£ £/spaces£print i, u"test" La version rot13: # coding: rot13 sbe v va enatr(5): £spaces£ £/spaces£cevag v, h"grfg" L'exécution : $ python test_rot13.py 0 test 1 test 2 test 3 test 4 test Petite remarque, si dans le code original l'on remplace _u"test"_ par _"test"_, et bien cela fonctionne toujours, mais cela affiche grfg en lieu et place de test. Cela vient d'une particularité de python. Les chaînes commençant par u représentent une suite de caractère unicode encodés dans le format du fichier. Ainsi lorsque python converti le code source en unicode, il prend le contenu de la chaine, le décode avec l'encodage spécifié, et forme ainsi une chaine unicode. Dans le cas d'une chaine sans le préfixe u, il s'agit (en python 2), d'une chaine d'octets purs qui doivent être insérés sans modifications dans le programme. Ainsi cette chaine est convertie en unicode, donnant le code _"test"_. Mais lors de l'exécution, le parseur va lire cette chaîne et se rappeler qu'en vrai il s'agit d'une chaine d'octets, et la reconvertir dans l'encodage d'origine. Comme dirait ma sainte grand-mère, _Oh my god !_ Bref, cette découverte m'a rendue heureuse. Et soudain mon cerveau malade et pervers s'est demandé jusque où cette perversion peut elle aller ? Je regarde un peu comment ce système fonction, et globalement c'est tout simple, python commence le traitement du code source en ASCII, et dès qu'il trouve un _#coding: name_, il passe toute la suite du fichier à la moulinette de décodage, celle-ci devant normalement lui générer une chaine unicode correspondant à un code source python valide. Mais il n'est écrit nul part que le contenu du fichier doit être un code source python valide ! En fait n'importe quel contenu (voir même pas de contenu) est possible, si la fonction de décodage génère du code valide. Ainsi je me suis lancé dans l'écriture d'un codec pour le [Brainfuck], langage idolé des foules. Je vous le copie paste en gros (attention, code ugly et hackish a mort, _Don't do this at home kids !_). Le fichier /usr/lib/python2.6/encodings/bf.py : import codecs indent = 0 started = False def bf_encode(input, errors='strict', £spaces£ £/spaces£filename='', mode=0666): £spaces£ £/spaces£return (str(input), len(input)) def bf_decode(input, errors='strict'): £spaces£ £/spaces£global indent, started £spaces£ £/spaces£if not input: £spaces£ £/spaces£return (u'', 0) £spaces£ £/spaces£content = input £spaces£ £/spaces£if not started: £spaces£ £/spaces£source = u''' from collections import defaultdict import sys data_ptr = 0 memory = defaultdict(int) £spaces£ £/spaces£'''.split(u'\n') £spaces£ £/spaces£started = True £spaces£ £/spaces£else: £spaces£ £/spaces£source = [] £spaces£ £/spaces£def add(s): £spaces£ £/spaces£source.append(u'\t' * indent + s) £spaces£ £/spaces£for char in content: £spaces£ £/spaces£if char == u'>': £spaces£ £/spaces£add(u'data_ptr += 1') £spaces£ £/spaces£ £spaces£ £/spaces£if char == u'<': £spaces£ £/spaces£add(u'data_ptr -= 1') £spaces£ £/spaces£ £spaces£ £/spaces£if char == u'+': £spaces£ £/spaces£add(u'memory[data_ptr] += 1') £spaces£ £/spaces£ £spaces£ £/spaces£if char == u'-': £spaces£ £/spaces£add(u'memory[data_ptr] -= 1') £spaces£ £/spaces£ £spaces£ £/spaces£if char == u'.': £spaces£ £/spaces£#add(u'sys.stdout.write(memory[data_ptr])') £spaces£ £/spaces£add(u'sys.stdout.write(chr(memory[data_ptr]))') £spaces£ £/spaces£ £spaces£ £/spaces£if char == u',': £spaces£ £/spaces£add(u'memory[data_ptr] = ord(sys.stdin.read(1))') £spaces£ £/spaces£if char == u'[': £spaces£ £/spaces£add(u'while memory[data_ptr]:') £spaces£ £/spaces£indent += 1 £spaces£ £/spaces£if char == u']': £spaces£ £/spaces£indent -= 1 £spaces£ £/spaces£return (u'\n'.join(source) + u'\n', len(input)) class Codec(codecs.Codec): £spaces£ £/spaces£def encode(self, input, errors='strict'): £spaces£ £/spaces£return bf_encode(input, errors) £spaces£ £/spaces£def decode(self, input, errors='strict'): £spaces£ £/spaces£return bf_decode(input, errors) class StreamWriter(Codec, codecs.StreamWriter): £spaces£ £/spaces£pass class StreamReader(Codec, codecs.StreamReader): £spaces£ £/spaces£pass def getregentry(): £spaces£ £/spaces£return (bf_encode, bf_decode, StreamReader, StreamWriter) Le fichier source à exécuter : # coding: bf ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. Et son execution : $ python test.py Hello World! Rendons hommage à un autre fou furieux qui à eu la même idée, mais qui s'en est servi pour remplacer l'indentation significative de python par un système à base d'accolades, [[http://timhatch.com/projects/pybraces/](http://timhatch.com/projects/pybraces/)]. Je me suis largement inspiré de son code pour écrire le mien. Pour conclure, cela ne sert à rien, c'est marrant, et on doit pouvoir écrire des préprocesseurs pour python avec ;) Je me suis bien marré, j'espère que vous aussi ;)

AltStyle によって変換されたページ (->オリジナル) /