Función rot13 corta – Python

Estoy buscando una función rot13 corta y genial en Python 😉 He escrito esta función:

def rot13(s): chars = "abcdefghijklmnopqrstuvwxyz" trans = chars[13:]+chars[:13] rot_char = lambda c: trans[chars.find(c)] if chars.find(c)>-1 else c return ''.join( rot_char(c) for c in s ) 

¿Alguien puede hacerlo mejor? Por ejemplo, apoyar caracteres en mayúsculas.

Aquí hay una solución maketrans / translate

 import string rot13 = string.maketrans( "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm") string.translate("Hello World!", rot13) # 'Uryyb Jbeyq!' 

Es muy sencillo:

 >>> import codecs >>> codecs.encode('foobar', 'rot_13') 'sbbone' 

Esto funciona en Python 2 (pero no en Python 3):

 >>> 'foobar'.encode('rot13') 'sbbone' 

Las funciones de maketrans y translate en el módulo de string son útiles para este tipo de cosas. Por supuesto, el método de encode en la respuesta de Amber es aún más práctico para este caso específico.

Aquí hay una solución general:

 import string def make_rot_n(n): lc = string.ascii_lowercase uc = string.ascii_uppercase trans = string.maketrans(lc + uc, lc[n:] + lc[:n] + uc[n:] + uc[:n]) return lambda s: string.translate(s, trans) rot13 = make_rot_n(13) rot13('foobar') # 'sbbone' 

Desde el módulo this.py ( import this ).

 d = {} for c in (65, 97): for i in range(26): d[chr(i+c)] = chr((i+13) % 26 + c) print "".join([d.get(c, c) for c in s]) 

A partir de Python 3.1, string.translate y string.maketrans ya no existen. Sin embargo, estos métodos se pueden utilizar con bytes lugar.

Así, una solución actualizada directamente inspirada en la de Paul Rubel es:

 rot13 = bytes.maketrans( b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", b"nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM") b'Hello world!'.translate(rot13) 

La conversión de string a bytes y viceversa se puede realizar con las funciones integradas de encode y decode .

Prueba esto:

 import codecs codecs.encode("text to be rot13()'ed", "rot_13") 

En python-3, el código- str que @amber mencionó se ha movido a la biblioteca estándar de codecs :

 > import codecs > codecs.encode('foo', 'rot13') sbb 

Una sola línea para rot13 una cadena S :

 S.translate({a : a + (lambda x: 1 if x>=0 else -1)(77 - a) * 13 for a in range(65, 91)}) 

Para valores arbitrarios, algo como esto funciona para 2.x

 from string import ascii_uppercase as uc, ascii_lowercase as lc, maketrans rotate = 13 # ROT13 rot = "".join([(x[:rotate][::-1] + x[rotate:][::-1])[::-1] for x in (uc,lc)]) def rot_func(text, encode=True): ascii = uc + lc src, trg = (ascii, rot) if encode else (rot, ascii) trans = maketrans(src, trg) return text.translate(trans) text = "Text to ROT{}".format(rotate) encode = rot_func(text) decode = rot_func(encode, False) 

Esto funciona para mayúsculas y minúsculas. No sé cuán elegante consideras que es.

 def rot13(s): rot=lambda x:chr(ord(x)+13) if chr(ord(x.lower())+13).isalpha()==True else chr(ord(x)-13) s=[rot(i) for i in filter(lambda x:x!=',',map(str,s))] return ''.join(s) 

Puede admitir letras mayúsculas en el código original publicado por el Sr. Walter alternando las letras mayúsculas y minúsculas.

 chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" 

Si observa que el índice de las letras mayúsculas son números pares, mientras que el índice de las letras minúsculas es impar.

  • A = 0 a = 1,
  • B = 2, b = 3,
  • C = 4, c = 4,

Este patrón impar y uniforme nos permite agregar de manera segura la cantidad necesaria sin tener que preocuparnos por el caso.

 trans = chars[26:] + chars[:26] 

La razón por la que agrega 26 es porque la cadena se ha duplicado en letras debido a las letras mayúsculas. Sin embargo, el cambio sigue siendo 13 espacios en el alfabeto.

El código completo:

 def rot13(s): chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" trans = chars[26:]+chars[:26] rot_char = lambda c: trans[chars.find(c)] if chars.find(c) > -1 else c return ''.join(rot_char(c) for c in s) 

SALIDA (Probado con python 2.7):

 print rot13("Hello World!") --> Uryyb Jbeyq! 

La siguiente función rot(s, n) codifica una cadena s con encoding ROT- n para cualquier entero n , con n defecto en 13. Se admiten tanto las letras mayúsculas como las minúsculas. Los valores de n sobre 26 o los valores negativos se manejan de manera apropiada, por ejemplo, cambiar en 27 posiciones es igual a cambiar en una posición. La deencoding se realiza con invrot(s, n) .

 import string def rot(s, n=13): '''Encode string s with ROT-n, ie, by shifting all letters n positions. When n is not supplied, ROT-13 encoding is assumed. ''' upper = string.ascii_uppercase lower = string.ascii_lowercase upper_start = ord(upper[0]) lower_start = ord(lower[0]) out = '' for letter in s: if letter in upper: out += chr(upper_start + (ord(letter) - upper_start + n) % 26) elif letter in lower: out += chr(lower_start + (ord(letter) - lower_start + n) % 26) else: out += letter return(out) def invrot(s, n=13): '''Decode a string s encoded with ROT-n-encoding When n is not supplied, ROT-13 is assumed. ''' return(rot(s, -n)) 
 def rot13(s): lower_chars = ''.join(chr(c) for c in range (97,123)) #ASCII az upper_chars = ''.join(chr(c) for c in range (65,91)) #ASCII AZ lower_encode = lower_chars[13:] + lower_chars[:13] #shift 13 bytes upper_encode = upper_chars[13:] + upper_chars[:13] #shift 13 bytes output = "" #outputstring for c in s: if c in lower_chars: output = output + lower_encode[lower_chars.find(c)] elif c in upper_chars: output = output + upper_encode[upper_chars.find(c)] else: output = output + c return output 

Otra solución con el cambio. Tal vez este código ayude a otras personas a entender mejor el rot13. No lo he probado completamente.

 from string import maketrans, lowercase, uppercase def rot13(message): lower = maketrans(lowercase, lowercase[13:] + lowercase[:13]) upper = maketrans(uppercase, uppercase[13:] + uppercase[:13]) return message.translate(lower).translate(upper) 

Ejercicio interesante 😉 creo que tengo la mejor solución porque:

  1. no se necesitan módulos, usa solo las funciones incorporadas -> no en desuso
  2. Puede ser utilizado como un forro
  3. basado en ascii, no se asignan secuencias de texto / cadenas, etc.

Python 2 y 3 (probablemente Python 1):

 def rot13(s): return ''.join([chr(ord(n) + (13 if 'Z' < n < 'n' or n < 'N' else -13)) if n.isalpha() else n for n in s]) def rot13_verbose(s): x = [] for n in s: if n.isalpha(): # 'n' is the 14th character in the alphabet so if a character is bigger we can subtract 13 to get rot13 ort = 13 if 'Z' < n < 'n' or n < 'N' else -13 x.append(chr(ord(n) + ort)) else: x.append(n) return ''.join(x) # crazy .min version (99 characters) disclaimer: not pep8 compatible^ def r(s):return''.join([chr(ord(n)+(13if'Z'n else-13))if n.isalpha()else n for n in s]) 

Encontré esta publicación cuando comencé a preguntarme sobre la forma más fácil de implementar rot13 en Python. Mis objectives fueron:

  • Funciona tanto en Python 2.7.6 como en 3.3.
  • Manejar tanto mayúsculas como minúsculas.
  • No utilizar ninguna biblioteca externa.

Esto cumple con los tres de esos requisitos. Dicho esto, estoy seguro de que no está ganando ningún concurso de golf de código.

 def rot13(string): CLEAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' ROT13 = 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm' TABLE = {x: y for x, y in zip(CLEAR, ROT13)} return ''.join(map(lambda x: TABLE.get(x, x), string)) if __name__ == '__main__': CLEAR = 'Hello, World!' R13 = 'Uryyb, Jbeyq!' r13 = rot13(CLEAR) assert r13 == R13 clear = rot13(r13) assert clear == CLEAR 

Esto funciona creando una tabla de búsqueda y simplemente devolviendo el carácter original para cualquier carácter que no se encuentre en la tabla de búsqueda.

Actualizar

Tengo que preocuparme por alguien que quiera usar esto para cifrar un archivo arbitrariamente grande (por ejemplo, unos pocos gigabytes de texto). No sé por qué querrían hacer esto, pero ¿y si lo hicieran? Así que lo reescribí como un generador. De nuevo, esto se ha probado tanto en Python 2.7.6 como en 3.3.

 def rot13(clear): CLEAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' ROT13 = 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm' TABLE = {x: y for x, y in zip(CLEAR, ROT13)} for c in clear: yield TABLE.get(c, c) if __name__ == '__main__': CLEAR = 'Hello, World!' R13 = 'Uryyb, Jbeyq!' r13 = ''.join(rot13(CLEAR)) assert r13 == R13 clear = ''.join(rot13(r13)) assert clear == CLEAR 

No podía dejar esta pregunta aquí sin una sola statement usando el operador de módulo.

 def rot13(s): return ''.join([chr(x.islower() and ((ord(x) - 84) % 26) + 97 or x.isupper() and ((ord(x) - 52) % 26) + 65 or ord(x)) for x in s]) 

Esto no es pythonico ni es una buena práctica, ¡pero funciona!

 >> rot13("Hello World!") Uryyb Jbeyq! 

También puedes usar esto también.

 def n3bu1A(n): o="" key = { 'a':'n', 'b':'o', 'c':'p', 'd':'q', 'e':'r', 'f':'s', 'g':'t', 'h':'u', 'i':'v', 'j':'w', 'k':'x', 'l':'y', 'm':'z', 'n':'a', 'o':'b', 'p':'c', 'q':'d', 'r':'e', 's':'f', 't':'g', 'u':'h', 'v':'i', 'w':'j', 'x':'k', 'y':'l', 'z':'m', 'A':'N', 'B':'O', 'C':'P', 'D':'Q', 'E':'R', 'F':'S', 'G':'T', 'H':'U', 'I':'V', 'J':'W', 'K':'X', 'L':'Y', 'M':'Z', 'N':'A', 'O':'B', 'P':'C', 'Q':'D', 'R':'E', 'S':'F', 'T':'G', 'U':'H', 'V':'I', 'W':'J', 'X':'K', 'Y':'L', 'Z':'M'} for x in n: v = x in key.keys() if v == True: o += (key[x]) else: o += x return o Yes = n3bu1A("N zhpu fvzcyre jnl gb fnl Guvf vf zl Zragbe!!") print(Yes) 

Solución corta:

 def rot13(text): return "".join([x if ord(x) not in range(65, 91)+range(97, 123) else chr(((ord(x)-97+13)%26)+97) if x.islower() else chr(((ord(x)-65+13)%26)+65) for x in text])