Short rot13 function - Python

PythonStringEncoding

Python Problem Overview


I am searching for a short and cool rot13 function in Python ;-) I've written this function:

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 ) 

Can anyone make it better? E.g supporting uppercase characters.

Python Solutions


Solution 1 - Python

It's very simple:

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

Solution 2 - Python

maketrans()/translate() solutions…

Python 2.x

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

Python 3.x

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

Solution 3 - Python

This works on Python 2 (but not Python 3):

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

Solution 4 - Python

The maketrans and translate methods of str are handy for this type of thing.

Here's a general solution:

import string

def make_rot_n(n):
    lc = string.ascii_lowercase
    uc = string.ascii_uppercase
    trans = str.maketrans(lc + uc,
                          lc[n:] + lc[:n] + uc[n:] + uc[:n])
    return lambda s: str.translate(s, trans)

rot13 = make_rot_n(13)

rot13('foobar')
# 'sbbone'

Solution 5 - Python

From the builtin module this.py (import this):

s = "foobar"

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]))  # sbbone

Solution 6 - Python

As of Python 3.1, string.translate and string.maketrans no longer exist. However, these methods can be used with bytes instead.

Thus, an up-to-date solution directly inspired from Paul Rubel's one, is:

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

Conversion from string to bytes and vice-versa can be done with the encode and decode built-in functions.

Solution 7 - Python

Try this:

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

Solution 8 - Python

In python-3 the str-codec that @amber mentioned has moved to codecs standard-library:

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

Solution 9 - Python

The following function rot(s, n) encodes a string s with ROT-n encoding for any integer n, with n defaulting to 13. Both upper- and lowercase letters are supported. Values of n over 26 or negative values are handled appropriately, e.g., shifting by 27 positions is equal to shifting by one position. Decoding is done with invrot(s, n).

import string

def rot(s, n=13):
    '''Encode string s with ROT-n, i.e., 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))

Solution 10 - Python

A one-liner to rot13 a string S:

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

Solution 11 - Python

For arbitrary values, something like this works for 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)

Solution 12 - Python

This works for uppercase and lowercase. I don't know how elegant you deem it to be.

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)

Solution 13 - Python

You can support uppercase letters on the original code posted by Mr. Walter by alternating the upper case and lower case letters.

chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"

If you notice the index of the uppercase letters are all even numbers while the index of the lower case letters are odd.

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

This odd-even pattern allows us to safely add the amount needed without having to worry about the case.

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

The reason you add 26 is because the string has doubled in letters due to the upper case letters. However, the shift is still 13 spaces on the alphabet.

The full code:

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)

OUTPUT (Tested with python 2.7):

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

Solution 14 - Python

def rot13(s):
	lower_chars = ''.join(chr(c) for c in range (97,123)) #ASCII a-z
    upper_chars = ''.join(chr(c) for c in range (65,91)) #ASCII A-Z
    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

Another solution with shifting. Maybe this code helps other people to understand rot13 better. Haven't tested it completely.

Solution 15 - Python

Interesting exercise ;-) i think i have the best solution because:

  1. no modules needed, uses only built-in functions --> no deprecation
  2. it can be used as a one liner
  3. based on ascii, no mapping dicts/strings etc.

Python 2 & 3 (probably 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<'n'or'N'>n else-13))if n.isalpha()else n for n in s])

Solution 16 - Python

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)

Solution 17 - Python

I found this post when I started wondering about the easiest way to implement rot13 into Python myself. My goals were:

  • Works in both Python 2.7.6 and 3.3.
  • Handle both upper and lower case.
  • Not use any external libraries.

This meets all three of those requirements. That being said, I'm sure it's not winning any code golf competitions.

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

This works by creating a lookup table and simply returning the original character for any character not found in the lookup table.

Update

I got to worrying about someone wanting to use this to encrypt an arbitrarily-large file (say, a few gigabytes of text). I don't know why they'd want to do this, but what if they did? So I rewrote it as a generator. Again, this has been tested in both Python 2.7.6 and 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

Solution 18 - Python

You can also use this also

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)

Solution 19 - Python

I couldn't leave this question here with out a single statement using the modulo operator.

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])

This is not pythonic nor good practice, but it works!

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

Solution 20 - Python

Short solution:

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])

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsvenwltrView Question on Stackoverflow
Solution 1 - PythonNazmul HasanView Answer on Stackoverflow
Solution 2 - PythonPaul RubelView Answer on Stackoverflow
Solution 3 - PythonAmberView Answer on Stackoverflow
Solution 4 - PythonarsView Answer on Stackoverflow
Solution 5 - PythonArtur GasparView Answer on Stackoverflow
Solution 6 - PythonbbcView Answer on Stackoverflow
Solution 7 - PythonBarrestView Answer on Stackoverflow
Solution 8 - PythonankostisView Answer on Stackoverflow
Solution 9 - PythonjeroenView Answer on Stackoverflow
Solution 10 - PythonwjvView Answer on Stackoverflow
Solution 11 - Pythonuser1543747View Answer on Stackoverflow
Solution 12 - PythonEratosthenesView Answer on Stackoverflow
Solution 13 - PythonDiazView Answer on Stackoverflow
Solution 14 - PythonDeaD_EyEView Answer on Stackoverflow
Solution 15 - PythonyammView Answer on Stackoverflow
Solution 16 - Pythonuser847988View Answer on Stackoverflow
Solution 17 - PythonDeaconView Answer on Stackoverflow
Solution 18 - PythonFrancis BanguraView Answer on Stackoverflow
Solution 19 - PythonZv_oDDView Answer on Stackoverflow
Solution 20 - PythonSyntaxErrorView Answer on Stackoverflow