Hi,
da ich keine Ahnung von Assembler habe...:
Kann jemand eine Funktion erstellen, mit der ich bei einem String prüfen kann ob er nur Zahlen enthält, also nur aus Ziffern und gegebenenfalls einem - davor, keine Kommas? Rückgabewert 1 oder 0 reicht.
Ich hab so eine Funktion in reinem Profan, aber muss die teilweise 25000 Mal aufrufen, was entsprechend dauert. Wäre Assembler da überhaupt schneller?
String auf Zahl prüfen in Inline-Assembler
-
-
-
Mal ein kleiner Versuch
Code
Alles anzeigenCLS Declare String Meldung, Wert Declare Int Ergebnis Wert = "15,346789" ASM "IsNum", 1 PUSH ESI MOV ESI, par1 // Stringadresse nach ESI XOR EAX, EAX // Register löschen schleife: LODSB // Byte von [ESI] nach AL OR AL, AL JZ ende CMP AL, $39 // vergleiche 39h JG raus0 // wenn größer, Abbruch CMP AL, $2C // vergleiche Komma JZ istkomma // JA, weiter CMP AL, $2E // vergleiche Punkt, auskommentieren, wenn nur auf Komma geprüft werden soll JZ istkomma // JA,weiter, auskommentieren, wenn nur auf Komma geprüft werden soll CMP AL, $30 JNL schleife raus0: // ist keine Zahl XOR EAX, EAX // Register löschen => Rückgabewert 0 JMP ok istkomma: ADD AH, 1 // Anzahl erkannter Kommas zählen JMP schleife ende: CMP AH, 2 // Anzahl Kommas JAE dkomma // 2 oder mehr MOV EAX, 1 // für gültige Zahl Rückgabewert 1 setzen JMP OK dkomma: MOV EAX, -1 ok: POP ESI ENDASM Ergebnis = IsNum(Addr(Wert)) If Ergebnis Meldung = "ist eine Zahl" Case Ergebnis = -1 : Meldung = Meldung + " mehrere Kommas!" Else Meldung = "enthält andere Zeichen" EndIf Print "Zahlentest " + Wert + " " + Meldung WaitInput
Es wird in der ASM-Funktion ein nullterminierter String erwartet. Als Komma kann das Komma oder gleichberechtigt der Punkt stehen. Unpassende Zeilen nötigenfalls auskommentieren mit // davor oder ganz löschen. Rückgabe ist 1, wenn es eine Zahl ist oder -1, wenn mehrere Kommas enthalten sind. Sind andere Zeichen enthalten, ist die Rückgabe 0. Kommas können an beliebiger Stelle innerhalb des Strings auftreten.
Dürfte auf jeden Fall schneller sein als reiner Profan-Code.Gruß Volkmar
-
Hallo Volkmar,
danke. Es geht mir um Ganzzahlen, also ohne Komma. Und das erste Zeichen darf auch ein "-" sein, also in Prinzip ein stinknormales Integer. Kannst du deine Routine bitte nochmal mofifizieren, da ich wirklich Null Ahnung von Assembler habe? -
Ja, OK, setz mich nochmal ran.
Gruß Volkmar
-
Vielen Dank im Voraus. Eine andere Frage folgt dann gleich danach...
-
Jetzt sind nur die Ziffern 0 bis 9 erlaubt, das Minus kann nur als erstes Zeichen auftreten, sonst ist es ein Fehler. Rückgabewert 0 = Fehler, 1 = gültiger Int
Code
Alles anzeigenCLS Declare String Meldung, Wert Declare Int Ergebnis Wert = "1534a6789" ASM "IsInt", 1 PUSH ESI MOV ESI, par1 // Stringadresse nach ESI XOR EAX, EAX // Register löschen LODSB // lade erste Ziffer von [ESI] nach AL OR AL, AL JZ ende CMP AL, $2d // vergleiche 2Dh (Minus) JNZ weiter schleife: LODSB // Byte von [ESI] nach AL weiter: OR AL, AL JZ ende CMP AL, $39 // vergleiche 39h JG raus0 // wenn größer, Abbruch CMP AL, $30 // vergleiche 30h JNL schleife // wenn nicht kleiner, weiter raus0: // ist keine Zahl XOR EAX, EAX // Register löschen => Rückgabewert 0 JMP ok ende: MOV EAX, 1 ok: POP ESI ENDASM Ergebnis = IsInt(Addr(Wert)) If Ergebnis Meldung = "ist ein Integer" Else Meldung = "enthält andere Zeichen" EndIf Print "Zahlentest " + Wert + " " + Meldung WaitInput
Gruß Volkmar
-
Vielen Dank,
es funktioniert. Ich bin von 2.6 auf 2.2 Sekunden runter. Mit Between() statt > und < auf 2 Sekunden.
Wenn ich so frech sein darf:
- Eine Modifikation: Der String darf nur Ziffern enthalten, ohne "-".
- Eine Modifikation: Der String darf nicht bloß ein "-" sein, sonst wird 0 zurückgegeben.
- Eine modifizierte Variante mit 2 Parametern: der zu prüfende String und ein String, der alle gültigen Zeichen enthält. -
Erst mal eine Korrektur zur IsInt
Code
Alles anzeigenASM "IsInt", 1 PUSH ESI MOV ESI, par1 // Stringadresse nach ESI XOR EAX, EAX // Register löschen OR ESI, ESI // Wenn Leerstring JZ raus0 // raus LODSB // lade erste Ziffer von [ESI] nach AL OR AL, AL JZ ende CMP AL, $2d // vergleiche 2Dh (Minus) JNZ weiter schleife: LODSB // Byte von [ESI] nach AL weiter: OR AL, AL JZ ende CMP AL, $39 // vergleiche 39h JG raus0 // wenn größer, Abbruch CMP AL, $30 // vergleiche 30h JNL schleife // wenn nicht kleiner, weiter raus0: // ist keine Zahl XOR EAX, EAX // Register löschen => Rückgabewert 0 JMP ok ende: MOV EAX, 1 ok: POP ESI ENDASM
Bitte die vorherige Form ersetzen. Die stürzt ab, wenn ein Leerstring übergeben wird.
IsIntL prüft auf Länge, es muß bei einem führenden - mindestens eine gültige Ziffer folgen. Kann allerdings auch 0 sein, auf Ausblenden führender Nullen habe ich verzichtet.
Code
Alles anzeigenASM "IsIntL", 1 PUSH ESI PUSH EBX MOV ESI, par1 // Stringadresse nach ESI XOR EAX, EAX // Register löschen XOR EBX, EBX // Stellenzähler löschen OR ESI, ESI // Wenn Leerstring JZ raus0 // raus LODSB // lade erste Ziffer von [ESI] nach AL OR AL, AL JZ raus0 CMP AL, $2d // vergleiche 2Dh (Minus) JNZ weiter schleife: LODSB // Byte von [ESI] nach AL weiter: OR AL, AL JZ ende CMP AL, $39 // vergleiche 39h JG raus0 // wenn größer, Abbruch CMP AL, $30 // vergleiche 30h JL raus0 // wenn kleiner, Abbruch INC EBX // Ziffernstelle zählen JMP schleife raus0: // ist keine Zahl XOR EAX, EAX // Register löschen => Rückgabewert 0 JMP ok ende: XOR EAX, EAX OR EBX, EBX // Wenn Stellenzähler leer JZ ok // raus MOV EAX, 1 // sonst Rückgabe auf 1 setzen ok: POP EBX POP ESI ENDASM
IsNum erlaubt nur die Ziffern 0 bis 9Code
Alles anzeigenASM "IsNum", 1 PUSH ESI MOV ESI, par1 // Stringadresse nach ESI XOR EAX, EAX // Register löschen OR ESI, ESI // wenn Leerstring JZ raus0 // raus schleife: LODSB // Byte von [ESI] nach AL weiter: OR AL, AL JZ ende CMP AL, $39 // vergleiche 39h JG raus0 // wenn größer, Abbruch CMP AL, $30 // vergleiche 30h JNL schleife // wenn nicht kleiner, weiter raus0: // ist keine Zahl XOR EAX, EAX // Register löschen => Rückgabewert 0 JMP ok ende: MOV EAX, 1 ok: POP ESI ENDASM
Und dann noch eine Funktion, da soll der Vorrat an gültigen Zeichen als zweiter String vorgegeben werden, wenn ich richtig verstanden habe. Dauert dann noch etwas, kommen erst im Laufe des Abends dazu.
Auf das Profan-Drumherum habe ich hier mal verzichtet, damit die Codes nicht zu lang werden. War ja nur zum Prüfen, Du baust es ja ohnehin bei Dir einGruß Volkmar
-
Tut´s der eingebaute MATCH$(RegEx$, String$) nicht? Ich komme HIER auf netto 2.5 Sekunden!
-
Jac wollte es mal in ASM probieren. Und mir tut's gut, mal wieder was zu machen. Bin schon ganz eingerostet und habe erst mal ein paar Abstürze produziert. Und dann auch noch vergessen, auf Leerstring zu prüfen
Gruß Volkmar
-
Volkmar: Danke für die neuen Funktionen. IsIntL drückt meinen Test nochmal auf 1.7 Sekunden (weil ich mir die Prüfung, ob der String nur aus einem "-" besteht, spare), das ist genial.
@p.specht: Bestimmt, aber ich führe die Funktion zum Teil 25000 mal aus und etliche anderen noch dazu. Keine Ahnung was wirklich schneller ist. Es geht um ein Prgramm, was mir meine neue Arbeit erleichtern soll und die Rechner bei mir auf Arbeit sind...nun ja...leicht antik. Ich selbst besitze einen Ryzen 7 1700X, das sind's 2.5 Sekunden, aber auf Arbeit wird schnell eine Viertelminute draus. -
Und nun mal noch Einen. Gestern Abend wollte er nicht mehr, aber nach dem Morgenkaffee habe ich mein Problem gesehen
Erster String ist der zu prüfende String und der zweite String enthält die Liste der gültigen Zeichen. Das Ergebnis ist 1, wenn jedes Zeichen aus String 1 an irgend einer Position in String 2 enthalten ist. Alle anderen Fälle liefern 0
Code
Alles anzeigenCLS Declare String Meldung, Wert, Ref Declare Int Ergebnis Wert = "11231ab2312e" Ref = "12345abcde" ASM "CharTest", 2 PUSH ESI PUSH EDI PUSH EDX MOV ESI, par1 // Stringadresse nach ESI MOV EDI, par2 // Stringadresse Referenz nach EDI OR ESI, ESI // Test auf Leerstring JZ fehler OR EDI, EDI // Test auf Leerstring JZ fehler XOR EAX, EAX // Register löschen MOV EDX, EDI // Referenzadresse merken schleife: MOV EDI, EDX // gemerkte Referenzadresse laden für neuen Durchlauf LODSB OR AL, AL // Stringende jz ok ischleife: MOV AH, [EDI] // Byte von Referenz OR AH, AH // wenn 0 JZ fehler // nicht gefunden, Fehler CMP AL, AH // Zeichen testen JZ schleife // ist gültig, nächstes Zeichen INC EDI // sonst innere Schleife hochzählen JMP ischleife // und innen weiter Fehler: XOR EAX, EAX JMP raus ok: MOV EAX, 1 raus: POP EDX POP EDI POP ESI ENDASM Ergebnis = CharTest(Addr(Wert), Addr(Ref)) If Ergebnis Meldung = "ist gültig" Else Meldung = "enthält andere Zeichen" EndIf Print "Zahlentest " + Wert + " " + Meldung WaitInput
Gruß Volkmar
-
Wow, vielen Dank, werde ich gleich testen. Das sind die Dinge die ich mal in einem anderen Thread meinte mit "Assemblerfunktionen sammeln" bzw. eine Bibliothek erzeugen.
-
Ist das eventuell auch in Assembler umsetzbar?
Code
Alles anzeigenproc ValidLine Parameters p$ casenot InStr("\t",p$):Return 0 declare valid%,q$ whileloop 8 q$=SubStr$(p$,&Loop,"\t") if q$="" valid%=SetBit(valid%,&Loop,1) else Select &Loop CaseOf 3,4 if Not(IsIntL(Addr(q$))) valid%=SetBit(valid%,&Loop,1) endif CaseOf 5 ifnot IsIntL(Addr(q$)) AND Between(Val(q$),0,359) Inc valid%,32 endif CaseOf 7,8 if q$<>"0" valid%=SetBit(valid%,&Loop,1) endif EndSelect endwhile endif Return valid% endproc
Die Funktion testet einen String, der aus 8 Teilstücken, getrennt durch \t bestehen soll wobei einige Abschnitte nur Zahlen enthalten dürfen und die letzten beiden 0 sein müssen.
-
Machbar ist das. Dauert allerdings etwas. Wird morgen oder übermorgen werden.
Gruß Volkmar
-
Nur wenn du viel Zeit und Lust hast, ich hab schon ein bisschen ein schlechtes Gewissen hier Leute für mein Projekt einzuspannen.
-
-
Hallo Heinz,
hab ich und muss ich mal probieren.In meinem Test dauert das Einlesen und Verarbeiten einer Cad-Datei etwa 2.4 Sekunden. Wenn ich ValidLine für jede Zeile weglasse, dann komme ich auf 0.8 Sekunden. Ich kann leider nicht zu sehr ins Detail gehen, da das Programm für meine Arbeit ist und da kommt natürlich das Betriebsgeheimnis ins Spiel...
Vielleicht wäre die Funktion noch besser (* am Anfang leitet einen Kommentar ein):
Code
Alles anzeigenproc ValidLine Parameters p$ declare valid%,q$ whileloop 8 q$=SubStr$(p$,&Loop,"\t") if q$="" valid%=SetBit(valid%,&Loop,1) else Select &Loop CaseOf 3,4 if Not(IsIntL(Addr(q$))) valid%=SetBit(valid%,&Loop,1) endif CaseOf 5 ifnot IsIntL(Addr(q$)) AND Between(Val(q$),0,359) Inc valid%,32 endif CaseOf 7,8 if q$<>"0" valid%=SetBit(valid%,&Loop,1) endif EndSelect endwhile endif Return valid% endproc
... auf \t wird schon im Programm geprüft.
-
Match ist leider langsamer und ein Move hat irgendwie auch nicht geholfen.
-
Aktuelle Variante:
Code
Alles anzeigenproc ValidLine Parameters q$ declare valid%,a$[],loop& a$[]=Explode(q$,"\t") For loop&,0,7 if a$[loop&]="" valid%=SetBit(valid%,Loop&,1) else Select Loop& CaseOf 2,3 ifnot IsIntL(Addr(a$[loop&])) valid%=SetBit(valid%,Loop&,1) endif CaseOf 4 ifnot IsIntL(Addr(a$[loop&])) AND Between(Val(a$[loop&]),0,359) Inc valid%,16 endif CaseOf 6,7 if a$[loop&]<>"0" valid%=SetBit(valid%,Loop&,1) endif EndSelect EndFor endif Return valid% endproc
Explode hat was gebracht, genauso wie beim Bit 0 beginnen statt bei 1. Das Auslesen im Hauptprogramm wurde natürlich auch angepasst. Alles in Allem spare ich so nochmal etwa 200 Millisekunden, nicht schlecht.
-
Jetzt mitmachen!
Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!