tags:

views:

673

answers:

6

Hi, I am looking for a way to represent '\n' with only one character. I am writing a program that uses dictionaries to 'encrypt' text. Because each character is represented in the dictionary, i am having a problem when my program gets to a '\n' in a string, but reads it as '\' 'n' . Is there alternate way to represent a newline, that is only one character? This is my code below, sorry if some of the indentation is messed up. I dont entirely understand how to input code into this window. :)

##################################
#This program will take an input and encrypt it or decrypt it
#A cipher is used to transform the text, which can either be
#input or from a text file.
#The cipher can be any letter a-z, as well as most commonly used special characters
#numbers and spaces are not allowed.
#For the text, a-z, most special characters, space, and new line may be used.
#no numbers can be encrypted.
##################################

#These three dictionaries are used during the encryption process.
keyDict={'a': 17,'b': 3,'c':16,'d':26,'e':6,'f':19,'g':10,'h':12,
         'i':22,'j':8,'k': 11,'l':2,'m':18,'n':9,'o':23,'p':7,
         'q':5,'r': 20,'s': 1,'t': 24,'u':13,'v':25,'w':21,'x':15,
         'y':4,'z': 14, ' ':42, '.':0,'!': 27, '@': 34, '#': 35, '%': 37,
         '$': 36, "'": 33,'&': 39, '*': 40, ',': 29, '.': 30, '~': 41, ';': 31,
         ':': 32, '?': 28, '^': 38}

refDict2={' ': 43,  'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'g': 7, 'f': 6, 'i': 9,
         'h': 8, 'k': 11, 'j': 10, 'm': 13, 'l': 12, 'o': 15, 'n': 14, 'q': 17,'\n': 42,
         'p': 16, 's': 19, 'r': 18, 'u': 21, 't': 20, 'w': 23, 'v': 22, 'y': 25,
         'x': 24, 'z': 26, ' ':0, '!': 27, '@': 34, '#': 35, '%': 37, '$': 36, "'": 33,
          '&': 39, '*': 40, ',': 29, '.': 30, '~': 41, ';': 31, ':': 32, '?': 28, '^': 38}

refDict={0: ' ', 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i',
         10: 'j', 11: 'k', 12: 'l', 13: 'm', 14: 'n', 15: 'o', 16: 'p', 17: 'q', 42:'\n',
         18: 'r', 19: 's', 20: 't', 21: 'u', 22: 'v', 23: 'w', 24: 'x', 25: 'y', 26: 'z', 43:' ',
         32: ':', 33: "'", 34: '@', 35: '#', 36: '$', 37: '%', 38: '^', 39: '&', 40: '*',
         41: '~', 27: '!', 28: '?', 29: ',', 30: '.', 31: ';'}


#switch1 reverses a list. It is its own inverse, so we don't need an unswitch function. 
def switch1(l):
    return l[::-1]



#switch2 takes a list as input and moves every fourth entry to the front
#so switch2([a,b,c,d,e,f,g]) returns ([a,e,b,c,d,f,g])
#unswitch2 undoes this, so unswitch2([a,e,c,d,f,g]) returns [a,b,c,d,e,f,g]

def switch2(l):
    List4 = []
    ListNot4 = []
    for i in range(0,len(l)):
        if i%4 == 0:
            List4.append(l[i])
        else:
            ListNot4.append(l[i])
    return List4+ListNot4

def unswitch2(l):
    num4 = len(l)/4 + 1
    fixedList = l[num4:]
    for i in range (0,num4):
        fixedList.insert(4*i,l[i])
    return fixedList



#switch3 takes a list as input and returns a list with the first half moved to the end.
#so switch3([a,b,c,d,e,f]) returns [d,e,f,a,b,c]
#for lists of odd length, switch3 puts the separation closer to the beginning of the list, so the
#middle entry becomes the first entry.
#For example, switch3([a,b,c,d,e,f,g]) returns [d,e,f,g,a,b,c]

def switch3(l):
    return l[len(l)/2:] + l[:len(l)/2]

def unswitch3(l):
    if len(l)%2==0:
        return switch3(l)
    else:
        return l[len(l)/2+1:] + l[:len(l)/2+1]




##################################
#This is the Crypt function.
##################################
def Crypt(text, cipher):
    counter=0
    text=text.lower()
    cipher=cipher.lower()
    keyValue=[]
    textValue=[]
    newValue=[]
    newString=''
    for letter in cipher:
        keyValue.append(keyDict[letter])

    for letter in text:
        textValue.append(refDict2[letter])

    for num in textValue:
        newValue.append(num+keyValue[counter%len(keyValue)])
        counter+=1

    newValue = switch1(newValue)
    newValue = switch2(newValue)
    newValue = switch3(newValue)

    for num in newValue:
        newString+=refDict[num%43]

    return newString

##################################
#This is the Decrypt function
##################################
def Decrypt(encryptedText, cipher):
    counter=0
    cipher=cipher.lower()
    keyValue=[]
    textValue=[]
    finalValue=[]
    finalString=''

    for letter in encryptedText:
        textValue.append(refDict2[letter])

    textValue = unswitch3(textValue)
    textValue = unswitch2(textValue)
    textValue = switch1(textValue)

    for letter in cipher:
        keyValue.append(keyDict[letter])

    for num in textValue:
        finalValue.append((num-keyValue[counter%len(keyValue)])%43)
        counter+=1


    for num in finalValue:
        finalString+=refDict[num]

    return finalString


##################################
#This is the user interface.
##################################

choice=raw_input('Would you like to: 1)Encrypt or 2)Decrypt?  Pick 1 or 2: ')

if choice=='1':
    textType=raw_input("Would you like to: 1)encrypt a text file or 2) input the text to be encrypted? Pick 1 or 2: ")

    if textType=='1':
        cryptName=raw_input( 'Please enter the name of the text file you would like to encrypt(eg. text.txt): ')
        newName=raw_input('Please name the file in which the encrypted text will be stored(eg. secret.txt):' )
        cipher=raw_input("Now enter your personal encryption key(eg. secret code):" )

        cryptFile=open(cryptName, 'r')
        newFile=open(newName, 'w')
        print >> newFile, Crypt(cryptFile.read(),cipher)
        cryptFile.close()
        newFile.close()
        print "Ok, all done!"
    elif textType=='2':
        inputText=raw_input('Ok, please input the text you would like to encrypt(eg. computers rock!): ')
        cipher=raw_input("Now enter your personal encryption key (eg. ultra secret code): ")
        if inputText=='': print 'Oops, no text was entered! Try again!'
        else:
            print Crypt(inputText, cipher)
    else:
        print 'Try again!'

elif choice=='2':
    textType=raw_input("Would you like to:1)decrypt a text file or 2) input the text to be decrypted? Pick 1 or 2: ")
    if textType=='1':
        decryptName=raw_input( 'Please enter the name of the text file you would like to decrypt(eg. text.txt): ')
        newName2=raw_input('Please name the file in which the decrypted text will be stored(eg. secret.txt):' )
        cipher=raw_input("Now enter the encryption key that was used to encrypt the file(eg. secret code):" )
        #Text decrypt
        decryptFile=open(decryptName, 'r')
        newFile2=open(newName2, 'w')
        print>> newFile2, Decrypt(decryptFile.read(),cipher)
        #other stuff
        #textTodecrypt=decryptFile.read()
        #newFile2.writelines(Decrypt(textTodecrypt, cipher))


        decryptFile.close()
        newFile2.close()
        print "Ok, all done!"

    elif textType=='2':
        inputText=raw_input('Ok, please input the text you would like to decrypt(eg. dig&ak:do): ')
        cipher=raw_input("Now enter the encryption key that was used to encrypt the text (eg. ultra secret code): ")
        if inputText=='': print 'Oops, no text was entered! Try again!'
        else:
            print Decrypt(inputText, cipher)

print "Have a nice day!"


#there is an issue with the newline character
+5  A: 

'\n' is one character. It is a new line escape character and is just a representation of the new line.

please rephrase your question in a readable way.

[Edit] I think I know what your problem is. I ran your program and it is working just fine. You are probably trying to pass '\n' to your program from the command line. That will not work!

You see, if you gave raw_input() this string: line1\nline2 it will escape the \n and make it \\n like this: 'line1\\nline2'

So a quick hacky fix is to find and replace '\\n' with '\n':

text.replace('\\n', '\n')

I don't like this. But it will work for you.

A much better way is to read multiple lines, like this:

input = raw_input('Please type in something: ')
lines = [input]
while input:
    input = raw_input()
    lines.append(input)

text = '\n'.join(lines)

print text
Nadia Alramli
Is it guaranteed? e.g. on Windows, when you write '\n' to a file? I ask that because IIRC Mircosoft's C runtime automatically converts '\n' to '\r\n'.
Bastien Léonard
im not sure what you mean. If i run the Crypt funtion on a bunch of text, with new lines, and then decrypt it; it works fine. If i try to do that with text files, it gets all mixed up. If that makes sense.
Great answer -- I think you and DBR are on the right track.
HanClinto
For clarity's sake (because people often get confused): text = text.replace('\\n', '\n')
David Berger
+4  A: 

I'm guessing that your real problem is not with reading in \n as a '\' 'n' -- internally, Python should automagically translate \n into a single character.

My guess is that the real problem is that your newlines are probably actually two characters -- carriage return ('\r') and newline ('\n'). Try handling \r in addition to \n, and I wonder if that won't make your problem go away.

HanClinto
Ok, i will give this a try right now. Sorry for the horrible formatting.
From running the script, it seems the problem is raw_input() treats \n as a literal \ and a literal n, and there is no mapping for \ in keyDict..
dbr
Great point, DBR -- I didn't run his script, but you did. I voted up your answer -- I think you've got the most likely response.
HanClinto
+1  A: 

You should probably take advantage of a number's numeric value to perform your encryption and avoid those big data structures that will fail for non-ascii text anyway.

apphacker
His encryption is merely a very simple character shuffle/replacement. I doubt binary encryption is a concern.
Brian
right. I am more just trying to create my own method. I realize it is not very complex, as compared to binary and hex etc.
+3  A: 

Newline character is a single character.

>>> a = '\n'
>>> print len(a)
1
>>> a = '\n\n\n'
>>> a[1]
'\n'
>>> len(a)
3
>>> len(a[0])
1

So you misunderstand what your problem is.

nosklo
+2  A: 

I'm new to Python, but there could be a way to simplify what you are doing by using the ord and chr functions to change characters to ASCII values and vice versa. Here's a link to the built-in function documentation in Python.

gnovice
+3  A: 

I assume the problem is if there is \n in the text to be decrypted, it breaks:

KeyError: \

module body   in untitled at line 166
function Crypt    in untitled at line 94

Basically, raw_input() returns a string containing two characters \ and n - and you have no mapping for \ thus the error.

The simplest solution is so simply replace the literal characters \n with the \n escape sequence

def Crypt(text, cipher):
    text.replace(r"\n", "\n")
    ...

The raw string r"\n" creates a string containing the literal character \ followed by n (the same as doing "\\n").

In a regular string "\n" it's treated as the escape-sequence for a new-line. So the above code-block replaces \n in text with an newline.

You may have to define a mapping for "\n" in your keyDict mapping.

Another solution would be to split the text using text.split(r"\n") and treat each line separately. Or as others have suggested, use ord() every character and deal with numbers, rather than making your own numerical mapping.

ord(c) Given a string of length one, return an integer representing the Unicode code point of the character when the argument is a unicode object, or the value of the byte when the argument is an 8-bit string. For example, ord('a') returns the integer 97, ord(u'\u2020') returns 8224. This is the inverse of chr() for 8-bit strings and of unichr() for unicode objects.

As the docs explains, chr() is the opposite, and will turn the number back into a ASCII or Unicode character:

chr(i) Return a string of one character whose ASCII code is the integer i. For example, chr(97) returns the string 'a'. This is the inverse of ord(). The argument must be in the range [0..255], inclusive; ValueError will be raised if i is outside that range.

Basically you would do..

def Crypt(text, cipher):
    keyvalue = [ord(x) for x in cipher)
    textvalue = [ord(x) for x in text]

..instead of the two for loops (the list-comprehension is basically the same as making a list, looping over each character in text or cipher, and appending to the list each time)

dbr
ok, i am giving this a try.thanks.
This sounds like it's the most likely answer. Well done, DBR.
HanClinto