A codificación permite representar catro tipos de datos, números enteiros, cadeas de caracteres (non necesariamente lexibles), listas e diccionarios.
Para saber o tipo de dato que é temos que ler o primer carácter, se é un 'i', e enteiro; 'l', lista; 'd', diccionario; e un número, cadea de caracteres.
def decode_torrent(f): c = f.read(1) if c == 'i': return decode_int(f) elif c == 'l': return decode_list(f) elif c == 'd': return decode_dict(f) elif c == 'e': return None # Xa veremos para que funciona :) elif c in map(str, range(10)): return decode_string(c, f) else: raise Exception('Invalid character ', c)
No caso de ser un enteiro o que atopamos é o número escrito en texto, acabado cunha 'e', por exemplo:
i1234eEntón, a rutina para decodificalo é simple:
def decode_int(f): nums = [] c = f.read(1) while c != 'e': nums.append(c) c = f.read(1) # Xuntamos tódo-los caracteres e convertímolos nun número return int(''.join(nums))
No caso dunha lista igual, lemos todos os elementos ata atopar unha 'e', se tiramos uso da función 'decode_torrent' queda ate máis sinxelo:
def decode_list(f): l = [] item = decode_torrent(f) while item != None: l.append(item) item = decode_torrent(f) return l
Se o tema a tratar son os diccionarios á complicación tampouco é excesiva, só temos que ler os elementos por pares, o primeiro será o índice e o segundo o valor, se o índice é unha 'e' pois xa non seguimos:
def decode_dict(f): d = {} key = decode_torrent(f) while key != None: value = decode_torrent(f) d[key] = value key = decode_torrent(f) return d
Por último, ó ler as cadeas atopamonos primeiro coa súa lonxutide, de novo expresada coma un número en texto e acabado cun ':', so temos que tomar ese número (chamemoslle 'lonx') e ler os seguintes 'lonx' caracteres:
def decode_string(n, f): # Lemos o tamanho l = [n] c = f.read(1) while c != ':': l.append(c) c = f.read(1) # Feito iso só queda ler lonxitude = int(''.join(l)) return f.read(lonxitude)
E xa está, un arquivo .torrent decodificado en menos de 50 liñas de código (formateado a man ;) ):
>>> decode_torrent(open('trisquel_5.5_i686.iso.torrent')) {'comment': 'Trisquel GNU/Linux 5.5 brigantia. i686 Installable Live CD', 'info': {'length': 729808896, 'piece length': 262144, 'name': 'trisquel_5.5_i686.iso', 'pieces': '(eliminado por brevedade, son os hashes das pezas do arquivo)'}, 'creation date': 1334183204, 'created by': 'mktorrent 1.0', 'announce': 'http://trisquel.info:6969/announce', 'url-list': ['http://cdimage.trisquel.info/trisquel-images/trisquel_5.5_i686.iso', 'http://gdsol.uta.cl/trisquel/iso/trisquel_5.5_i686.iso', 'http://us.archive.trisquel.info/iso/trisquel_5.5_i686.iso', 'http://es.gnu.org/~ruben/trisquel/trisquel_5.5_i686.iso', 'http://ftp.linux.org.tr/trisquel/iso/trisquel_5.5_i686.iso', 'http://ftp.rediris.es/mirror/Trisquel/iso/trisquel_5.5_i686.iso', 'http://nl.cdimage.trisquel.info/trisquel_5.5_i686.iso', 'ftp://in.archive.trisquel.info/trisquel-iso/trisquel_5.5_i686.iso', 'http://ibelin.mx.gnu.org/trisquel_5.5_i686.iso', 'http://ftp.linux.org.tr/trisquel/iso/trisquel_5.5_i686.iso']} >>>
No hay comentarios:
Publicar un comentario