0

im trying to rewrite every character in a file with rot 13 and im stuck, im not sure how to go through the file and look at every character and not worry about the spaces between paragraphs

# [import statements]
import q2fun
# [constants]

# [rest of program code]
f = open("rot-13.txt", "w", encoding="utf-8")
result = q2fun.rot13(f)

def rot13(f):

    f.seek(0)
#   y = 0
    result = ""
    for char in f:
        x = ord(char)
        if 97 <= x < 110 or 65 <= x < 78:
#           string[y]=char.replace(char, chr(x+13))
            char = char.replace(char, chr(x + 13))
            result = result + char
            print(char)
            continue
#           y+=1
        elif x >= 110 or 78 <= x < 91:
#           string[y]=char.replace(char, chr(x-13))
            char = char.replace(char, chr(x - 13))
            print(char)
            result = result + char
            continue
#           y+=1
        result = result + char
    return result
user2980776
  • 123
  • 1
  • 1
  • 6
  • 1
    Are you trying to learn how to do this manually, or just looking for the easiest way to do it? – abarnert Nov 25 '13 at 19:51
  • 1
    Well, you're already pretty close; your `rot13` algorithm is actually correct, it's just that you're not reading files correctly. So, it's worth learning how to fix it. But it's also worth learning how to use the "batteries included" in Python, and if you need this for a real use rather than just for learning, it's better to use the batteries. So, without knowing which one you want, the question really needs two answers… – abarnert Nov 25 '13 at 20:07
  • 1
    What's the difference between writing it yourself and using something that's already built in? Using something built in means less work for you. It means you get code that's been debugged in various edge cases you didn't think about, and sometimes optimized as well. It means someone reading your code can look up the function you call in the docs instead of having to find and read the implementation. But writing it yourself means you get to learn how it works and what it really does. – abarnert Nov 25 '13 at 20:20
  • ill like the second option – user2980776 Nov 25 '13 at 20:24

3 Answers3

2
import codecs


with open("plaintext.txt") as f_in, open("rot-13.txt", "w") as f_out:
    f_out.write(codecs.encode(f_in.read(),"rot_13"))

with open("rot-13.txt") as encoded:
    print (codecs.decode(encoded.read(),"rot_13"))

See the documentation on the codecs module, or the built-in help in the interactive interpreter, for an explanation of these functions. See the documentation on the Standard Encodings (and following sections), or import the encodings module and use the built-in help, to see a list of encodings you can use with these functions.

Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
  • they got rid of rot13 builtin encoding? – Joran Beasley Nov 25 '13 at 19:45
  • is there a way to do it on python 3.x – user2980776 Nov 25 '13 at 19:46
  • @JoranBeasley yes! I don't know why, it was a great feature. – rlms Nov 25 '13 at 19:47
  • It was eliminated because it doesn't `encode` from `str` to `bytes`, so therefore there's no way to access it from `str.encode` (which is guaranteed to return a `bytes`). You have to use the codec explicitly. There' been some talk of coming up with easier ways to use "transformer" codecs that convert str->str or bytes->bytes or bytes->str (like `hex`), but so far no concrete proposals have come out. – abarnert Nov 25 '13 at 19:49
  • 2
    @user2980776: For Python 3.x compatibility, use `import codecs; codecs.encode(f_in.read(), 'rot_13')` instead. – jazzpi Nov 25 '13 at 19:50
  • can i see it in a example? – user2980776 Nov 25 '13 at 19:52
  • 1
    there noow it will work in python 3 :) .. and it will work just as well for rot15 ;P – Joran Beasley Nov 25 '13 at 19:52
  • 1
    @JoranBeasley: No. According to http://stackoverflow.com/questions/3269686/short-rot13-function, string.maketrans was removed in Python 3.2. It only works if directly applied to a string object. – jazzpi Nov 25 '13 at 19:55
  • I see ... ok :P I will fix with your suggestion :) – Joran Beasley Nov 25 '13 at 19:56
  • i keep getting an erro when i try to do it that says FileNotFoundError: [Errno 2] No such file or directory: 'plaintext.txt' – user2980776 Nov 25 '13 at 20:01
  • 1
    @user2980776: Obviously you have to have a file named `plaintext.txt` in your current working directory if you want to encode a file named `plaintext.txt`. If you want to use a different file, use a different filename. – abarnert Nov 25 '13 at 20:05
  • 2
    It would be better to show this using a `with` statement. Besides being a good habit to instill in novices, it also turns this from four lines of file management plus one line of actual work into one line of file management plus one line of actual work, making it clearer what's important here. – abarnert Nov 25 '13 at 20:08
  • I agree , however I dont use python 3 and always screw up the `with x as y, z as a` syntax( is that right?) – Joran Beasley Nov 25 '13 at 20:14
  • i got it do run but the rot-13.txt file is not changing – user2980776 Nov 25 '13 at 20:15
  • @JoranBeasley: Yes, it's right. And it's also right in Python 2, so I don't know why you don't understand it. – abarnert Nov 25 '13 at 20:15
  • @user2980776 its still not working tells me nothing ... it should work fine assuming plaintext.txt is in the same directory as the script ... or is it a different error? ... it also needs to have plaintext in the file – Joran Beasley Nov 25 '13 at 20:15
  • @abarnert Im pretty sure its not in 2.6 – Joran Beasley Nov 25 '13 at 20:16
  • @JoranBeasley: No, it was added in 2.7. But so what? Not teaching people 3.x features because a lot of people are still using 2.7, and will be for a few more years, is one thing; not teaching people 2.7 features is another… – abarnert Nov 25 '13 at 20:17
  • when i run the function nothing happens to the rot-13.txt fie, its stil using the regular alphabet – user2980776 Nov 25 '13 at 20:17
  • no I agree I just didnt wnat to get that syntax wrong ... im editing it now that you told me that was right :P – Joran Beasley Nov 25 '13 at 20:17
  • @user2980776 put `hello world` into the "plaintext.txt" file, after you run this it will be encoded and written into rot-13.txt ... – Joran Beasley Nov 25 '13 at 20:20
  • i try to do it but it just deletes all i have in there – user2980776 Nov 25 '13 at 20:23
  • what? ... it will overwrite whatever is in rot-13.txt with the encoded text from plaintext.txt – Joran Beasley Nov 25 '13 at 20:25
2

If you just want to do this as easily as possible, use the rot_13 codec, as described in Joran Beasley's answer.

If you want to know how to do it manually, or what was wrong with your existing code, I can explain. You're actually very close.

If f is a file, for char in f: iterates over the lines of the file, not the characters. If you want to iterate over the characters one by one, either loop around f.read(1), or read the whole thing into a string with s = f.read() and then iterate over s.

If you fix that, your program now works as written. However, it's more complicated than necessary.

First, char = char.replace(char, chr(x + 13)) is unnecessary. str.replace searches the string, substitutes the replacement character for all instances of the search character, and returns the resulting string. But you don't need any of that—you're searching all one character, replacing the one instance of the search character with the resulting character, and returning the resulting character as a one-character string—in other words, the same string you already had. All you want to do here is char = chr(x + 13).

Also, you can remove the three separate result = result + char and continue; all three conditions lead to the same thing.

Also, instead of comparing x to ordinal values (which are hard to read, and easy to get wrong), you can just compare char to character values. So:

def rot13(f):
    s = f.read()
    result = ""
    for char in s:
        x = ord(char)
        if 'a' <= char <= 'm' or 'A' <= char <= 'M':
            char = chr(x + 13)
        elif 'n' <= char <= 'z' or 'N' <= char <= 'Z':
            char = chr(x - 13)
        result = result + char
    return result

You can simplify your comparisons further by using str.lower:

        if 'a' <= char.lower() <= 'm':

(This replaces the if 'a'… line in the above code, and you do the same thing for the elif 'n'… line.)

You can simplify things even further by using the collections in the string class:

        if char in string.ascii_letters:
            if char.lower() <= 'm':
                char = chr(x + 13)
            else:
                char = chr(x - 13)

(This replaces the whole if/elif block.)

Or, if you know that the % (mod/remainder) operator does, you can simplify it even further: rot13(ch) is just (ch+13) % 26 (where ch is a letter number from 0 to 25, which you can get with ord(char) % 32). The usual C implementations take advantage of this, and you can write them more clearly with the divmod function in Python. But I'll leave that as an exercise for the reader.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 2
    meh you have more patience than me :) great answer – Joran Beasley Nov 25 '13 at 20:07
  • so isthe third one like the first one or do you need both – user2980776 Nov 25 '13 at 20:11
  • @user2980776: The simplifications don't replace the entire function; they just replace part of the code. I've added comments in parentheses to explain exactly what parts you replace. – abarnert Nov 25 '13 at 20:17
  • i did it and it says that s=f.read() is io.UnsupportedOperation: not readable – user2980776 Nov 25 '13 at 20:26
  • @user2980776: Well, what `f` are you passing it? It has to be an open, readable file object. In your original code, you're passing it a file opened in write-only mode (`'w'`), which will raise this exception. You may need to go over [Reading and Writing Files](http://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files) in the tutorial to get the basics. – abarnert Nov 25 '13 at 20:56
0

An old-school implementation with python3


def ROT_13(realText):
    outText = ""
    cryptText = []
    step = 13
    uppercase = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    lowercase = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
    puncuation = [",","'",":","!","~","`","@","#","$","%","^","&","*","(",")","-","_","+","=","<",">","/","?",";","\\","|","{","}","[","]"]

    for eachLetter in realText:
        if eachLetter in uppercase:
            index = uppercase.index(eachLetter)
            crypting = (index + step) % 26
            #print("{}  index={} Crypt={}".format(eachLetter,index,crypting))
            cryptText.append(crypting)
            newLetter = uppercase[crypting]
            outText += (newLetter)
        elif eachLetter in lowercase:
            index = lowercase.index(eachLetter)
            crypting = (index + step) % 26
            #print("{}  index={} Crypt={}".format(eachLetter,index,crypting))
            cryptText.append(crypting)
            newLetter = lowercase[crypting]
            outText += (newLetter)
        elif eachLetter in puncuation:
          outText += eachLetter 
        else:
          outText += " "
    return outText

You may call it like this

code = ROT_13("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
print(code)

to get the result

NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm

it even handles spaces, special characters as , # and CAPS

code2 = ROT_13("it even handles spaces, special characters as , # and CAPS")

to get

"vg rira unaqyrf fcnprf, fcrpvny punenpgref nf , # naq PNCF"
Vinod Srivastav
  • 3,644
  • 1
  • 27
  • 40