Eingabe vorschreiben

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
doggy
User
Beiträge: 22
Registriert: Freitag 19. September 2014, 10:32

Freitag 19. September 2014, 11:26

Guten Tag,

Ich habe ein Programm für eine PIN-Eingabe geschrieben. Diese wird mit einer Textdatei verglichen, die wie folgt aussieht ca.:

Code: Alles auswählen

Norbert 1234
Herbert 5678
usw.
Nun will ich abfragen ob der PIN der eingegeben wird in der Datei vorhanden ist. Es klappt alles soweit, bis auf das ich nicht vorschreiben kann, dass die Eingabe 4-Stellig aus zahlen sein muss. Ansonsten soll Falscher PIN ausgegeben werden.

Hier das Script:

Code: Alles auswählen

while True:
    try:
        a = int(input("Geben die die Zahlen ein"))
        b = open("pin.txt").read()
        if str(a) in b:
            print("Hallo")
        else:
            print("Falscher PIN")

    except:
        print("Fehler")
Die einrückungen sind natürlich richtig im echten Programm.
Vielen Dank
Zuletzt geändert von Hyperion am Freitag 19. September 2014, 12:06, insgesamt 1-mal geändert.
Grund: Code in Python-Code Tags gesetzt.
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Freitag 19. September 2014, 11:40

Hallo und willkommen im Forum!

Dein Programm hat so einige Problemchen. Als erstes fällt das try-except-Konstrukt auf. Verwende *niemals* eine except ohne die Angabe der genauen Ausnahme. Sonst werden alle Fehler verschluckt. Unter Umständen auch Programmierfehler. Dann geht irgend etwas in deinem Programm schief, dein Programm befindet sich anschließend in einem ungültigen Zustand und irgendwo kracht es dann (oder im schlimmsten Fall nicht). Solche Fehler sind extrem schwer zu finden.

Weiter musst du bei try-except beachten, dass die try-Blöcke so wenig Code wie möglich enthalten sollten. In deinem Fall wäre das nur die Umwandlung des Strings in einen Integer nicht mehr. Die Eingabe gehört da schon nicht mehr rein, der ganze restliche Code so wie so nicht. Denn auch da könnten Ausnahmen ausgelöst werden, welche du gar nicht behandeln willst.

Dann solltest du dir angewöhnen vernünftige Namen zu wählen. "a" und "b" sagen gar nichts aus. Da musst du jedes Mal nachschauen, was diese Namen überhaupt bedeuten. Wenn das steht "pin", dann ist die Sache sofort klar.

Dateien solltest du mittels with-Statement öffnen, dann werden diese, auch im Fehlerfall, wieder geschlossen.

Dein Test der PIN ist auch nicht korrekt. Du suchst die PIN in der gesamten Datei. Das heißt, dass für jeden Benutzer jede vorhandene PIN akzeptiert wird. Es ist sogar noch schlimmer. Es könnte auch Benutzer geben, die Ziffern enthalten. Auch die würden als korrekte PIN erkannt werden. Du musst deine Datei Zeilenweise verarbeiten. Oder gleich ein vernünftiges Format, wie csv, wählen. Dafür gibt es in Python auch ein Modul.

Zu deinem eigentlichem Problem: Du kannst die Länge von Strings abfragen, damit kannst du dann feststellen, ob auch vier Zeichen eingegeben wurden. Dieser Test ist aber an sich überflüssig. Wenn du den Test korrekt Implementierst, nicht mit "in" sonder mittels Gleichheit, dann musst du die Länge nicht extra testen.

Edit: Ach ja, das Forum unterstützt Code-Tags, damit wird dein Code vernünftig angezeigt. Der Button dazu befindet sich über dem großen Textfeld und heißt "Code". Dann kann man dein Programm auch vernünftig lesen.
Das Leben ist wie ein Tennisball.
Benutzeravatar
MagBen
User
Beiträge: 786
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Freitag 19. September 2014, 12:12

Eine 4-stellige Zahl ist größer als 999 und kleiner als 10000, das kannst Du prüfen:

Code: Alles auswählen

def ist_4_stellig(zahl):
    return 999<zahl<10000

print ist_4_stellig(  567)
print ist_4_stellig( 1567)
print ist_4_stellig(11567)
Der 10er Logarithmus einer 4-stellige Zahl ist größer-gleich 3.0 und kleiner als 4.0:

Code: Alles auswählen

import math
def ist_4_stellig(zahl):
    return int(math.log10(zahl))==3

print ist_4_stellig(  567)
print ist_4_stellig( 1567)
print ist_4_stellig(11567)
Der String aus dem Du die Zahl erzeugst sollte 4 Zeichen lang sein, auch das kannst Du prüfen ...
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

Freitag 19. September 2014, 12:31

Man könnte auch einfach die unsinnige Umwandlung in eine Zahl sein lassen.
doggy
User
Beiträge: 22
Registriert: Freitag 19. September 2014, 10:32

Freitag 19. September 2014, 13:20

Vielen Dank für die Antworten.

Zunächst an EyDu:

Ja, ich bin auch absoluter Anfänger. Ich habe vor zwei Wochen begonnen Python mit kaum Vorahnung zu lernen. Ich bin eigentlich gelernter Mechatroniker und muss nun in Rahmen eines Projektes die Steuerung einer Elektroladestation Programmieren. Das Script, welches ich hier gezeigt habe ist auch lediglich ein Test, welchen ich später wenn es funktioniert ordentlich im Programm einbinde und dann auch ordentlich benenne. Ansonsten nehme ich mir deine Tipps dankend zu Herzen und versuche sie bei meinem Programm zu bedenken.


Nun zu MagBen.

Vielen Dank, ich werde versuchen deinen Tipp einzubinden. Darüber könnte ich Abfragen, dass die Eingabe korrekt und 4-stellig ist.


Und dann noch Blackjack.

Ich kann die Umstellung nicht wegnehmen, noch nicht. Würde ich dies tun, könnte in dem Programm dieser Form soger nur mit der eingabe der Eingabetaste ein "okay" ausgelöst werden.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Freitag 19. September 2014, 13:27

BlackJack hat geschrieben:Man könnte auch einfach die unsinnige Umwandlung in eine Zahl sein lassen.
Eben, denn dann wird das Validieren kinderleicht:

Code: Alles auswählen

def is_pin_valid(pin):
    return len(pin) == 4 and all(c.isdigit() for c in pin)

def test_is_pin_valid():
    assert is_pin_valid("1234"), "'1234' ist ein gültiger PIN"
    assert not is_pin_valid("123"), "'123' hat nur drei Stellen und ist damit zu kurz!"
    assert not is_pin_valid("12345"), "'12345' hat fünf Stellen und ist damit zu lang!"
    assert not is_pin_valid("a234"), "'a234' enthält andere Zeichen als Ziffern!"
(Ja man sollte eigentlich ein Unittest Framework dafür nehmen ;-) )
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3))
assert encoding_kapiert
doggy
User
Beiträge: 22
Registriert: Freitag 19. September 2014, 10:32

Freitag 19. September 2014, 13:32

xD Kinderleicht, aufjedenfall :) Obwohl ich ein 400 Seiten Buch in 2 Wochen gewälzt habe, sagen mir die Befehle nicht viel, welche du dort auflistest. Ich kanns mir aber ca. denken
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Freitag 19. September 2014, 13:37

Und weil ich so wenig in Python mache in letzter Zeit habe ich das natürlich viel komplizierter ausgedrückt als notwendig, da ``isdigit`` natürlich auf *strings* arbeitet, also auch auf mehrstelligen :oops:

Korrigiert also so:

Code: Alles auswählen

def is_pin_valid(pin):
    return len(pin) == 4 and pin.isdigit()
Dank der Test-Funktion kann ich mir ziemlich sicher sein, dass das auch so klappt, wie es soll :-) Verbuchen wir obiges also unter dem Gesichtspunkt "Refactoring" des TDD Ansatzes :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3))
assert encoding_kapiert
doggy
User
Beiträge: 22
Registriert: Freitag 19. September 2014, 10:32

Freitag 19. September 2014, 14:09

Bitte nicht vergessen^^ Ich bin blutiger Anfänger. Wo binde ich nun diese Funktion in mein Programm ein, damit sie dafür sorgt das nur 4-Stellige Eingaben verarbeitet werden?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Freitag 19. September 2014, 14:13

Äh... na da, wo Du die PIN prüfen willst‽

Du hast doch schon andere Funktionen *aufgerufen* (``print``, ``open``, ``int``, usw.), was ist denn dann so schwer daran, eine selbst gebaute aufzurufen?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3))
assert encoding_kapiert
BlackJack

Freitag 19. September 2014, 14:16

@doggy: Na da wo Du testest ob die PIN in Deiner Textdatei steht, kannst Du mit der Funktion zusätzlich testen ob die PIN den Regeln für PINs entspricht. Man kann zwei Tests logisch mit ``and`` oder ``or`` verknüpfen.
doggy
User
Beiträge: 22
Registriert: Freitag 19. September 2014, 10:32

Freitag 19. September 2014, 14:16

Ach. Hatte die korrigierte Version nicht gelesen. Dann ist es natürlich auch kein Problem. Dank dir
doggy
User
Beiträge: 22
Registriert: Freitag 19. September 2014, 10:32

Freitag 19. September 2014, 14:29

Nun klappen alle Funktionen im Programm. Ich denke mal, dass es noch etwas groß ist. Ich will später das gesamte Verifizierungsprogramm darin laufen lassen unter der Funktion in der nun Pin ist korrekt geprintet wird.

Code: Alles auswählen

from time import sleep

def is_pin_valid(PIN):
    return len(PIN) == 4 and PIN.isdigit()

while True:
    
    korrekt = 0

    for i in range(1,6):
        
        try:
            PIN = input("Bitte geben sie Ihren Pin ein: ")
            
            
        except:
            print("Es dürfen nur Zahlen verwendet werden!")
            continue
        
        data = open("pin.txt").read()
        
        if PIN in data and is_pin_valid(PIN):
            
            print("Der PIN war korrekt")
            korrekt = 1
            sleep(5)
            continue
                
        else:
            
            print("Dieser Pin ist nicht korrekt")

    
    if korrekt != 1:
        print("5 Fehlversuche!")
        sleep(5)
Sleep soll eine gewisse Wartezeit zwischen den später mit Tkinker angezeigten Bildern ermöglichen und die Variable korrekt ist lediglich etwas tricksen, damit nicht nach 5 korrekten eingaben auch die Fehlerversuch anzeige kommt
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Freitag 19. September 2014, 15:01

``range(1, 6)`` kannst du in diesem Fall einfacher als ``range(5)`` schreiben. Da du den Wert nicht brauchst, kannst du es dir auch einfach machen ;-)

"PIN" sollte besser "pin" heißen, schau dir dazu mal PEP 8 an. Konstanten werden in Python durchgängig in Großbuchstaben geschrieben, daher verwirrt deine Schreibweise etwas.

Dein try-except-Statement ist total sinnlos, der except-Block kann nie betreten werden.

Du hast meinen Tipp zum Öffnen von Dateien noch nicht beachtet. Auch ignorierst du weiterhin den Benutzernamen. Jede vier (oder mehrstellige) Zahl in deiner Datei ist nun eine gültige Eingabe.

Das continue in Zeile 27 ist sinnlos, das macht nichts. Du willst wahrscheinlich break verwenden. Teste deine Programme ;-)

"korrekt" ist etwas umständlich. Teile dein Programm in sinnvolle Funktionen auf, dann sparst du dir diesen unnötigen Schritt.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Freitag 19. September 2014, 15:46

Neben allen anderen Kritikpunkten stören mich auch die vielen Leerzeilen! Wozu muss nach jedem Block eine Leerzeile davor und dahinter stehen? Finde ich nicht wirklich hilfreich und iirc verstößt das auch gegen PEP8.

@doggy: Du solltest die Daten nur *einmal* einlesen müssen; im Moment passiert das ja bei jedem Versuch...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3))
assert encoding_kapiert
Antworten