Verschlüsselungsprogramm - Caesar

Code-Stücke können hier veröffentlicht werden.
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

Hallo EyDu :-)
Ersteinmal: Vielen vielen Dank!
EyDu hat geschrieben:[…], aber warum machst du eine Unterscheidung zwischen Groß- und Kleinbuchstaben. Das kann man alles in einem Rutsch erledigen, auch wenn das Ergebnis dann eventuell ein wenig anders ist. […]
Okay, ich werde dann mal überlegen.
Möchtest du deine Codierung beibehalten, dann solltest du doppelten Code in Funktionen auslagen.
Was hat das mit Codierung zu tun?
Und die ganzen "j" wirst du durch die enumerate-Funktion los.
`enumerate` habe ich kurz nachdem ich das Programm am Anfang geschrieben habe kennengelernt, und habe danach nicht daran gedacht, da es ja so schon gut ist.
Und natürlich möchtest du nicht 26 als magische Zahl benutzen, sonder die Länge der Strings.
Wieso? Ist das etwa bei anderen möglicherweise anders? In der Doku steht:
The lowercase letters 'abcdefghijklmnopqrstuvwxyz'. This value is not locale-dependent and will not change.
Außerdem hätte ich noch etwas zum Nachdenken:

Code: Alles auswählen

>>> alpha = string.ascii_lowercase
>>> offset = 3
>>> dict(zip(alpha, alpha[offset:]+alpha[:offset]))
{'a': 'd', 'c': 'f', 'b': 'e', 'e': 'h', 'd': 'g', 'g': 'j', 'f': 'i', 'i': 'l', 'h': 'k', 'k': 'n', 'j': 'm', 'm': 'p', 'l': 'o', 'o': 'r', 'n': 'q', 'q': 't', 'p': 's', 's': 'v', 'r': 'u', 'u': 'x', 't': 'w', 'w': 'z', 'v': 'y', 'y': 'b', 'x': 'a', 'z': 'c'}
>>> 
Habe ich auch heute erst kennen gelernt, beim Lesen des Tutorials. Interessant, danke.
Im string-Modul gibt es übrigens auch eine translate-Funktion.
Danke, aber ich krieg es nicht hin, damit umzugehen :|
Sebastian
Jakob
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

derdon hat geschrieben:Am einfachsten bleibt aber immer noch str.encode :D

Code: Alles auswählen

>>> import string
>>> string.printable
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
>>> string.printable.encode('rot13')
'0123456789nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
Super, nur irgendwie ein bisschen unnütze, wenn man nicht nur Rot13 machen will :roll:
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

So, jetzt habe ich es nach EyDus Tipps umgeschrieben:
http://paste.pocoo.org/show/289109/
Nur das mit ``zip()`` und ``string.translate()`` habe ich noch nicht gemacht.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

nomnom: War ja auch nicht ernst gemeint ;)
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

derdon hat geschrieben:

Code: Alles auswählen

>>> import string
>>> string.printable
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
>>> string.printable.encode('rot13')
'0123456789nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
ROT-13 ist mir eigentlich zu unsicher. Ich hatte eher an die doppelte Verschlüsselungsstärke gedacht. Hat schon mal jemand ROT-26 implementiert?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

/me hat geschrieben:Hat schon mal jemand ROT-26 implementiert?
Viel zu unsicher! Ich nehm 2 Mal ROT13.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Wenn ich deinen Code anpassen müsste, würde ich so vorgehen:

Code: Alles auswählen

def encrypt_caesar(shift, plain, alpha=string.ascii_letters):
    cipher = []

    for char in plain:
        for i, element in enumerate(alpha):
            if char == element:
                charnumber = (i + shift) % len(alpha)
                chipher.append(string.ascii_letters[charnumber]
                break
        else:
            cipher.append(char)

    return "".join(cipher)
Dabei ändert sich dann natürlich die Codierung, da, bei einer Verschiebung von 1, das "z" nicht mehr auf das "a" fällt, sondern auf das "A".

Das du "len" statt einer magischen Zahlen benutzen solltest hat ganz einfache Gründe: Die 26 sagt erstmal gar nichts aus und bei etwas komplexeren Programmen wirst du in zwei Wochen nicht mehr wissen, für was die Zahl steht. Und nun stelle dir vor, das du dein Programm erweiter möchtest: vielleicht etwas ganz abgefahrenes, wie das Hinzunehmen von Ziffern ;-) An wie vielen Stellen müsstest du dein Programm ändern, wenn du so etwas durchführen willst? Im Idealfall bei einer: du passt nur das Alphabet an, der Rest geht automatisch. Bei deiner alten Lösung müsstest du an mehreren Stellen basteln und darfst keine vergessen.

Der nächste Schritt bei dir ist, von der for-Schleife weg zu kommen und ein Dictionary einzusetzen (oder zunächst einmal die find-Methode auf Strings). Dann solltest du dir noch die get-Methode auf Dictionaries anschauen, mit etwas Überlegung kannst du dir auch den Sonderfall für nicht zu kodierende Zeichen einsparen.

Und zum translate:

Code: Alles auswählen

>>> translation = string.maketrans("abcdefghij", "0123456789")
>>> string.translate("Spam and Eggs", translation)
'Sp0m 0n3 E66s'
Das Leben ist wie ein Tennisball.
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

/me hat geschrieben:
derdon hat geschrieben:

Code: Alles auswählen

>>> import string
>>> string.printable
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
>>> string.printable.encode('rot13')
'0123456789nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
ROT-13 ist mir eigentlich zu unsicher. Ich hatte eher an die doppelte Verschlüsselungsstärke gedacht. Hat schon mal jemand ROT-26 implementiert?
Hm, ich denke da an sowas:

Code: Alles auswählen

def encrypt(plain_text):
    cipher = plain_text
    return cipher

def decrypt(cipher):
    plain_text = cipher
    return plain_text

text = raw_input('Type in your text! ')
while text != '':
    text += raw_input('> ')
print "Encrypted:", encrypt(text)
print "Decrypted:", decrypt(text)
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

EyDu hat geschrieben: Dabei ändert sich dann natürlich die Codierung, da, bei einer Verschiebung von 1, das "z" nicht mehr auf das "a" fällt, sondern auf das "A".
Das möchte ich aber auf keinen Fall :/
Das du "len" statt einer magischen Zahlen benutzen solltest hat ganz einfache Gründe: Die 26 sagt erstmal gar nichts aus und bei etwas komplexeren Programmen wirst du in zwei Wochen nicht mehr wissen, für was die Zahl steht […]
Oh, ja, stimmt!
Der nächste Schritt bei dir ist, von der for-Schleife weg zu kommen und ein Dictionary einzusetzen (oder zunächst einmal die find-Methode auf Strings).
Ich habe mal das Programm umgeschrieben, dass es ``str.find()`` benutzt:
http://paste.pocoo.org/show/290419/
Dann solltest du dir noch die get-Methode auf Dictionaries anschauen, mit etwas Überlegung kannst du dir auch den Sonderfall für nicht zu kodierende Zeichen einsparen.

Und zum translate:

Code: Alles auswählen

>>> translation = string.maketrans("abcdefghij", "0123456789")
>>> string.translate("Spam and Eggs", translation)
'Sp0m 0n3 E66s'
Das kommt später dann, muss jetzt los ;)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das charnumber=0 kannst du einsparen.
Das Leben ist wie ein Tennisball.
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

So, mit Dictionary:
http://paste.pocoo.org/show/290515/
Aber ich fühle mich immer schlecht, wenn ich Code von anderen fast 1:1 übernehme (auch wenn ich es verstanden hab). :|
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hat es einen Grund, dass du zwei Dictionaries verwendest? ;-)
Das Leben ist wie ein Tennisball.
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

EyDu hat geschrieben:Hat es einen Grund, dass du zwei Dictionaries verwendest? ;-)
Ich denke ja. Ich möchte, dass Groß- und Kleinschreibung richtig bleiben!11!1!einself!1!1 ;-)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Code: Alles auswählen

translation = dict(zip(l+u, l[shift:]+l[:shift]+u[shift:]+u[:shift]))
Das Leben ist wie ein Tennisball.
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

EyDu hat geschrieben:

Code: Alles auswählen

translation = dict(zip(l+u, l[shift:]+l[:shift]+u[shift:]+u[:shift]))
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah! :idea:
http://paste.pocoo.org/show/290544/ <-- Neuer Code
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Da Du Dich mit Verschiebechiffren beschäftigst, hier mal als Anregung mit veränderlichem Offset und Schlüssel (Rotorstellungen):

Code: Alles auswählen

from string import printable
from collections import deque

CHARS = printable

class Rotor(deque):
    def __init__(self, chars, period, start, direction):
        self.start = chars[start]
        self.period = period
        self.actual_step = 0
        self.direction = direction
        deque.__init__(self, chars)
        while self[0] != self.start:
            self.rotate(1)
    def get_char(self, num):
        self.actual_step += 1
        if self.period <= self.actual_step:
            self.actual_step = 0
            self.rotate(self.direction)
        return self[num]
        
class NotEnigma(object):
    def __init__(self, it, chars=CHARS):
        self.chars = chars
        self._rotors = [Rotor(chars, period, start, direction)
                        for period, start, direction in it]
    def translate(self, s):
        for rotor in self._rotors:
            s = ''.join(rotor.get_char(self.chars.index(i)) for i in s)
        return s

s = 'Hello World!'
ne1 = NotEnigma(((1, 5, -7), (2, 13, -17)))
encrypted = ne1.translate(s)
print encrypted                # --> '":1b5/%/ ~%

ne2 = NotEnigma(((1, -5, 7), (2, -13, 17)))
print ne2.translate(encrypted) # --> Hello World!
Damit gewinnst Du Sicherheit, da Häufigkeitsgipfel geglättet werden. Das Prinzip ist an die Enigma angelehnt. Diese hatte allerdings noch ein paar mehr technische Einzelheiten, die z.T. der Sicherheit zuträglich als auch abträglich waren (unterschiedliche Zeichenverdrahtungen pro Scheibe, Umkehrscheibe, Steckerbrett).
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Und jetzt erinnerst du dich noch einmal an "dict.get" ;-)

Code: Alles auswählen

    l = string.ascii_lowercase
    u = string.ascii_uppercase

    cipher = []
    translation = dict(zip(l+u, l[shift:]+l[:shift]+u[shift:]+u[:shift]))
    for char in plain:
        cipher.append(translation.get(char, char)

return "".join(cipher)[/code]

Wo bei du die Schleife gleich als List Comprehension schreiben könntest:

Code: Alles auswählen

cipher = [translation.get(char, char) for char in plain]
Oder mit map:

Code: Alles auswählen

cipher = map(lambda x: translation.get(x, x), plain)
Oder gleich zum return:

Code: Alles auswählen

return "".join(translation.get(char, char) for char in plain)
Das Leben ist wie ein Tennisball.
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

EyDu hat geschrieben:Und jetzt erinnerst du dich noch einmal an "dict.get" ;-)

Code: Alles auswählen

    l = string.ascii_lowercase
    u = string.ascii_uppercase

    cipher = []
    translation = dict(zip(l+u, l[shift:]+l[:shift]+u[shift:]+u[:shift]))
    for char in plain:
        cipher.append(translation.get(char, char)

return "".join(cipher)[/code]
Dieses default hab ich beim Lesen der Doku nicht verstanden :-/
Wo bei du die Schleife gleich als List Comprehension schreiben könntest:

Code: Alles auswählen

cipher = [translation.get(char, char) for char in plain]
Oder mit map:

Code: Alles auswählen

cipher = map(lambda x: translation.get(x, x), plain)
Oder gleich zum return:

Code: Alles auswählen

return "".join(translation.get(char, char) for char in plain)
Wow, dann wird der Code ja gleich total überschaubar … vielen vielen vielen vielen Dank, EyDu :-)

Edit #1:
Das ist übrigens die finale Version gewesen :D
Antworten