Seite 1 von 1

Zahl und Text trennen

Verfasst: Sonntag 20. November 2022, 15:45
von Pitwheazle
Bei den Aufgaben zu Umfang und Fläche von Figuren, möchte ich sicherstellen, dass die Schülerinnen und Schüler auch die richtige Einheit verwenden. Zur Auswertung müsste ich Zahl und Text trennen, das habe ich noch nicht wirklich hinbekommen. Wenn ich sicherstellen könnte, dass ein Leerzeichen dazwischen wäre, die Eingabe immer eine Ganzzahl wäre oder der erste Buchstabe immer gleich, würde ich das ja vielleicht noch hinbekommen. Es könnten aber so unterschiedliche Eingaben wie "24mm²" oder "2,4cm" sein. Mihilfe des Forums hier ist es mir schon gelungen den Zahlenwert auszulesen.

Code: Alles auswählen

zahl_eingabe = [float(s) for s in re.findall(r'-?\d+\.?\d*', eingabe)]
und:

Code: Alles auswählen

            zahl_loe = [float(s) for s in re.findall(r'-?\d+\.?\d*', loe)]
            zahl_eingabe = [float(s) for s in re.findall(r'-?\d+\.?\d*', eingabe)]
            try:
                if zahl_eingabe[0] == zahl_loe[0]:
                    return 0.5, "Die Zahl stimmt, die Einheit aber nicht - das ergibt einen halben Punkt Abzug!"
            except:
                return -1, ""
funktioniert auch ... wie aber bekomme ich die Einheit z.B. "m" oder "cm²". Damit würde ich gerne eine Rückmeldung geben, das z.B. die Längeneinheit mit der Flächeneinheit verwechselt wurde.

Re: Zahl und Text trennen

Verfasst: Sonntag 20. November 2022, 16:17
von Sirius3
Und schon wieder ein nackte except, das die Fehlersuche unmöglich macht. Variablennamen sollten nicht kryptische Abkürzungen enthalten. Wenn Du loesung meinst, warum schreibst Du sinn nur los?
findall ist falsch, wenn man nur eine Zahl erwartet. Deinen regulären Ausdruck musst du doch nur um das Pattern für die Einheit erweitern.

Re: Zahl und Text trennen

Verfasst: Sonntag 20. November 2022, 16:34
von Pitwheazle
Sirius3 hat geschrieben: Sonntag 20. November 2022, 16:17 Und schon wieder ein nackte except, das die Fehlersuche unmöglich macht.
Hier ging es mir doch gar nicht um Fehlersuche - ich weiß nicht so recht, was ich sonst noch an Code einfügen soll/kann.
Sirius3 hat geschrieben: Sonntag 20. November 2022, 16:17 Variablennamen sollten nicht kryptische Abkürzungen enthalten. Wenn Du loesung meinst, warum schreibst Du sinn nur los?
Ok, geht es nur um "loesung" statt "loe" oder was meinst du mit "... warum schreibst Du sinn nur los"?
Sirius3 hat geschrieben: Sonntag 20. November 2022, 16:17 findall ist falsch, wenn man nur eine Zahl erwartet.
wie ändere ich das?
Sirius3 hat geschrieben: Sonntag 20. November 2022, 16:17 Deinen regulären Ausdruck musst du doch nur um das Pattern für die Einheit erweitern.
wie? (ich fühle mich auch von den regulären Ausdrücken etwas überfordert)

Re: Zahl und Text trennen

Verfasst: Sonntag 20. November 2022, 17:03
von __deets__
Du hast dir mit dem nackten try/except ein gefaehrliches Muster angewoehnt, dass dir auch schon an anderer Stelle auf die Fuesse gefallen ist. Lass das einfach. Wenn du Fehlerbehandlung machen willst, musst du *immer* etwas der Art

Code: Alles auswählen

try:
    irgendwas = tuwas()
except EineKonkreteAusnahme, OderMehrere:
   ...
machen.

Was die regulaeren Ausdruecke angeht: das ist ein komplexes Thema. Dem kannst du dich versuchen mit Seiten wie https://regexr.com/ zB versuchen zu naehern. Hier mal ein kleines Beispiel, dass ich dir gebaut habe:

https://regexr.com/72q3t

Achtung, die Einheiten-Angabe ist etwas subtil. Aus technischen gruenden (greedy vs non greedy matching) muss das mm vor dem m kommen. Kann man in Python auch anders loesen (immer versuchen greedy zu matchen).

Alternativ statt solcher konkreter Einheiten kannst du auch einen (\w*) (Wort-Zeichen, 0-nmal) verwenden, und die erst in einem zweiten Schritt validieren. Das ist ggf. einfacher.

Re: Zahl und Text trennen

Verfasst: Sonntag 20. November 2022, 17:38
von Pitwheazle
Uih, das schafft mein armer alter Kopf nicht!
Wie findest du das:

Code: Alles auswählen

            loesung_getrennt=loesung.split()
            x_liste = ["c","d","m"]
            for x in x_liste:
                if x in eingabe:
                    eingabe_getrennt=eingabe.split(x)
                    eingabe_getrennt[1] = x + eingabe_getrennt[1]
?
(Ich habe in meiner Lösung ein Leerzeichen zwischen Zahl und Einheit eingefügt)

Nachtrag: Das klappt noch nicht ganz, aus "10mm²" wird "['10', 'm', '²']" - da muss ich noch drüber hirnen! (Das verstehe ich noch nicht)

Jetzt geht es (aber sicher nicht wirklich elegant):

Code: Alles auswählen

            loesung_getrennt=loesung.split()
            if "c" in eingabe:
                eingabe_getrennt=eingabe.split("c")
                eingabe_getrennt[1] = "c"+ eingabe_getrennt[1]
            elif "d" in eingabe:
                eingabe_getrennt=eingabe.split("d")
                eingabe_getrennt[1] = "d"+ eingabe_getrennt[1] 
            elif "m" in eingabe:
                eingabe_getrennt=eingabe.split("m")
                eingabe_getrennt[1] = "m"+ eingabe_getrennt[1] 
            eingabe_getrennt[0]=eingabe_getrennt[0].replace(",",".") 
 

Re: Zahl und Text trennen

Verfasst: Sonntag 20. November 2022, 18:19
von __deets__
Das problem ist die Eingabe des Leerzeichens. Das ist eine unangenehme Randbedingung. Wenn dir regex Zuviel ist, kannst du auch mit zb isdigit deine Zeichenkette zerteilen. Also alles, was digit ist, in ein Töpfchen, und den Rest danach in ein anderes.

Re: Zahl und Text trennen

Verfasst: Montag 21. November 2022, 00:10
von Kebap
Wie wäre es, die Benutzer um Eingabe in zwei Feldern zu bitten, einmal die Zahl, einmal die Einheit?

Re: Zahl und Text trennen

Verfasst: Montag 21. November 2022, 08:05
von Sirius3
Ich würde hier ganz einfach die Zahl vom ganzen Rest trennen:

Code: Alles auswählen

def parse_number_with_unit(text):
    match = re.fullmatch('\s*([-+]\d+[.,]\d*)\s*(.*)', text)
    if not match:
        raise ValueError(text)
    number, unit = match.groups()
    return float(number.replace(',', '.')), unit

Re: Zahl und Text trennen

Verfasst: Montag 21. November 2022, 15:55
von Pitwheazle

Code: Alles auswählen

            if "c" in eingabe:
                eingabe_getrennt=eingabe.split("c")
                eingabe_getrennt[1] = "c"+ eingabe_getrennt[1]
            elif "d" in eingabe:
                eingabe_getrennt=eingabe.split("d")
                eingabe_getrennt[1] = "d"+ eingabe_getrennt[1] 
            elif "m" in eingabe:
                eingabe_getrennt=eingabe.split("m")
                eingabe_getrennt[1] = "m"+ eingabe_getrennt[1] 
... sieht ja irgendwie blöd aus. Ich müsste die Schleife dazu bringen nach dem ersten Fund abzubrechen, da bräuchte es ein "exit for":

Code: Alles auswählen

            x_liste = ["c","d","m"]
            for x in x_liste:
                if x in eingabe:
                    eingabe_getrennt=eingabe.split(x)
                    eingabe_getrennt[1] = x + eingabe_getrennt[1]
                    exit for

Re: Zahl und Text trennen

Verfasst: Montag 21. November 2022, 16:35
von noisefloor
Hallo,

??? - welche Schleife? Die if-elif Kaskade wird nach dem 1. Treffer und der Ausführung des zugehörigen Befehlsblocks doch verlassen.

Gruß, noisefloor

Re: Zahl und Text trennen

Verfasst: Montag 21. November 2022, 17:32
von __blackjack__
@Pitwheazle: ``EXIT FOR`` heisst in Python ``break``.

`x` und `x_liste` sind keine guten Namen. Grundatentypen sollten nicht in Namen stehen, das ändert sich gerne mal während der Entwicklung und dann hat man entweder irreführende, falsche Namen im Quelltext, oder muss den durchgehen und überall betroffene Namen ändern. Statt ``["c", "d", "m"]`` könnte da ja auch ``("c", "d", "m")`` stehen, oder einfach ``"cdm"``.

Das mit `split()` und dann das Trennzeichen wieder an einen Teil anfügen ist etwas kompliziert IMHO. Ich fände es ”natürlicher” wenn man den Index des Buchstabens sucht und dann per slicing die beiden Teile erstellt. Was man auch noch behandeln muss ist der Fall das es nichts zum trennen gibt. Da könnte man für die Einheit beispielsweise einfach die leere Zeichenkette annehmen. Ungetestet:

Code: Alles auswählen

            for buchstabe in "cdm":
                try:
                    index = eingabe.index(buchstabe)
                except IndexError:
                    pass
                else:
                    wert_text, einheit = eingabe[:index], eingabe[index:]
                    break
            else:
                wert_text, einheit = eingabe, ""
Wobei das Vorgehen an sich nicht wirklich robust ist, weil das davon ausgeht, das es niemals Einheiten geben kann, in denen die getesteten Buchstaben nicht als erstes Zeichen nach der Zahl stehen.

Re: Zahl und Text trennen

Verfasst: Montag 21. November 2022, 18:04
von Pitwheazle
noisefloor hat geschrieben: Montag 21. November 2022, 16:35 ??? - welche Schleife? Die if-elif Kaskade wird nach dem 1. Treffer und der Ausführung des zugehörigen Befehlsblocks doch verlassen.
Die Schleife steht untendrunter (daher der Doppelpunkt).
__blackjack__ hat geschrieben: Montag 21. November 2022, 17:32 @Pitwheazle: ``EXIT FOR`` heisst in Python ``break``.
Das habe ich so schon ausprobiert.

Code: Alles auswählen

            for x in x_liste:
                if x in eingabe:
                    eingabe_getrennt=eingabe.split(x)
                    eingabe_getrennt[1] = x + eingabe_getrennt[1]
                    break
            print(eingabe_getrennt)        
... dabei bin ich einfach mal davon ausgegangen, dass die Schleife nach dem ersten Fund verlassen wird. "Print" ergibt bei Eingabe von "22mm²" aber wieder "['22', 'm', '²']". Das hatte ich ja oben auch schon mal so. Oder kann man das "break" so nicht anwenden?

__blackjack__ hat geschrieben: Montag 21. November 2022, 17:32 Wobei das Vorgehen an sich nicht wirklich robust ist, weil das davon ausgeht, das es niemals Einheiten geben kann, in denen die getesteten Buchstaben nicht als erstes Zeichen nach der Zahl stehen.
Nun ich weiß ja, dass keine anderen Einheiten vorkommen können. Zunächst überprüfe ich, ob in der Einagbe überhaupt eine Einheit eingegeben wurde, falls nicht erfolgt ein Hinweis und es kann die Eingabe entsprechend ergänzt werden. Dann überprüfe ich, ob die Eingabe meiner vorgegebenen Lösung entspricht, falls nicht, überprüfe ich, ob die Zahl richtig ist und gebe für diese richtige Zahl einen halben Punkt. Falls eine vollkommen andere Eingabe erfolgt, gehe ich davon aus, dass die Eingabe falsch ist - auch wenn z.B. km eingegeben wurde, damit habe ich jetzt kein Problem, mir geht es hauptsächlich darum, dass die Kids Längen und Flächeneinhaietn richtig zuordnen:

Code: Alles auswählen

            try:
                if float(eingabe_getrennt[0]) == float(loesung_getrennt[0]):
                    return 0.5, "Die Zahl stimmt, die Einheit aber nicht - das ergibt einen halben Punkt Abzug! Richtig wäre: " + loesung_getrennt[1]
            except:
                return -1, ""

Re: Zahl und Text trennen

Verfasst: Montag 21. November 2022, 19:21
von noisefloor
Hallo,
Nun ich weiß ja, dass keine anderen Einheiten vorkommen können.
Wie denn? Du hast doch ein Textfeld, d.h. da kann man grundsätzlich alles angeben. Also z.B. auch "zzz" als Einheit, obwohl eine Flächeneinheit gesucht ist. Oder hast du clientseitig schon eine Eingabevalidierung via JavaScript?

Gruß, noisefloor

Re: Zahl und Text trennen

Verfasst: Montag 21. November 2022, 19:56
von __blackjack__
@Pitwheazle: Das ``break`` verlässt die Schleife beim ersten Fund. Das Problem mit "22mm²" ist ein anderes, was eigentlich offensichtlich ist. Was erwartest Du was "22mm²".split("m") als Ergebnis liefert? Probier das einfach mal in einer interaktiven Python-Shell aus. Ein weiterer Grund warum `split()` hier nicht wirklich die Wahl sein sollte.

Re: Zahl und Text trennen

Verfasst: Dienstag 22. November 2022, 13:17
von Pitwheazle
@noisefloor: OK, ich hätte genauer sagen müssen, "ich weiß, welche Einheiten in richtigen Antworten vorkommen können". Ich hatte ja auch beschrieben, dass ich anstelle von "22 cm²" "22 Käsekuchen" nicht mit einem Teilpunkt bewerten werde.

@__blackjack__:

Code: Alles auswählen

                    eingabe_getrennt=eingabe.split(x,1)
                    eingabe_getrennt[1] = x + eingabe_getrennt[1]
                    break
... funktioniert jetzt aber so, wie ich es mir vorgestellt habe. Ich glaube das lasse ich jetzt so.

Re: Zahl und Text trennen

Verfasst: Dienstag 22. November 2022, 13:25
von Sirius3
@Pitwheazle: die bessere Lösung wäre es, mit regulären Ausdrücken zu arbeiten, anstatt selbst einen Parser zu schreiben. In der Zeit, in der Du Dir hier diese komplizierte Spit-Logik zusammengeschreiben hast, wäre auch ein Einarbeiten in reguläre Ausdrücke möglich gewesen, und Du hättest noch was für später gelernt gehapt.