[Algorithmik] 6stellige Zahlen aus einem string filtern.

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.
Benutzeravatar
SPUTNiK
User
Beiträge: 3
Registriert: Samstag 17. März 2012, 19:58

Samstag 17. März 2012, 21:14

Hallo zusammen,

ich habe aus Langeweile den nachfolgenden code geschrieben um alle sechsstelligen Zahlen aus einem string zu filtern. Er tut seinen job, nur ist er meiner Meinung nach ziemlich lame :D und schlecht erweiterbar noch dazu (sodass beispielsweise auch weitere x-stellige Zahlen gefiltert werden können).

Deshalb meine Frage an euch: Wie würdet Ihr das Problem (ohne RegEx) lösen?

Code: Alles auswählen

def isdigit(value):
 if((value>="0") and (value<="9")):
  return 1
 else:
  return 0

string = "ABC156458ABC01234567ABCABC431747ABC61"
x = list(string + ".")

for i in range(0, len(x)):
 if isdigit(x[i]):
  if isdigit(x[i+1]) and isdigit(x[i+2]) and isdigit(x[i+3]) and isdigit(x[i+4]) and isdigit(x[i+5]) and isdigit(x[i+6]):
   x[i+1]=x[i+2]=x[i+3]=x[i+4]=x[i+5]=x[i+6]="::"

  elif (isdigit(x[i+1]) and isdigit(x[i+2]) and isdigit(x[i+3]) and isdigit(x[i+4]) and isdigit(x[i+5])) and (x[i-1] != "::"):
   
   print "Hit:" + x[i]+x[i+1]+x[i+2]+x[i+3]+x[i+4]+x[i+5]
   x[i+1]=x[i+2]=x[i+3]=x[i+4]=x[i+5]="."
Zuletzt geändert von Anonymous am Samstag 17. März 2012, 22:02, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
EyDu
User
Beiträge: 4872
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Samstag 17. März 2012, 22:04

Code: Alles auswählen

>>> import re
>>> re.findall("(?=(\d{6}))", "ABC156458ABC01234567ABCABC431747ABC61")
['156458', '012345', '123456', '234567', '431747']  
Edit: Vielleicht noch ein paar Anmerkungen:
- Der Beitrag hat nicht so richtig etwas mit Algorithmik zu tun
- Deine isdigit-Funktion hat gleich mehrere Probleme:
* zum einen ist sie überflüssig, da Strings bereits eine Methode isdigit besitzen. Die sollte man nicht noch einmal implementieren.
* Der Test ist auch nicht besonders klug. Was ist, wenn der Benutzer "0a" übergibt. Bei dir kommt dann raus, das es sich um eine Ziffer handelt
* Es gibt True und False, dafür sollten nicht 1 und 0 gebraucht werden.
* Die Klammern beim if sind überfüssig und sollten weggelassen werden
- Weiter solltest du nichts an den Namen ``string`` binden, da es sich um einen vorhandenen Typen handelt, welchen du damit verdeckst.
- Konstanten werden in Python durchgängig groß geschrieben (schaue dir mal PEP 8 an)
- Das ``x = list(string + ".")`` ist unnötig, was willst du damit bezwecken?
- wenn du ``for i in range(0, len(x)):`` verwendest, dann machst du etwas falsch. Zum einen ist die 0 als Startwert überflüssig, zum anderen solltest du ``enumerate`` verwenden, wenn du den index brauchst.
- Wenn du anfänst zu nummerieren oder immer das selbe Muster schreibst (+1, +2, +3, ...) dann machst du etwas falsch. Da solltest du generischer arbeiten, zum Beispiel mit Listen, Tupeln oder, in diesem Fall, mit einer Schleife. Insgesamt ist an dem Code sehr viel redundant.
- Außerdem sollte eine Zeile nicht mehr als 80 Zeichen haben.

Sebastian
Zuletzt geändert von EyDu am Samstag 17. März 2012, 22:15, insgesamt 1-mal geändert.
Das Leben ist wie ein Tennisball.
BlackJack

Samstag 17. März 2012, 22:12

@SPUTNiK: Ohne regulären Ausdruck würde ich das gar nicht lösen, denn so einer wäre hier die einfachste, kompakteste und wohl auch sehr nachvollziehbare und verständliche Lösung. Dein's verstehe ich auf Anhieb nicht so ganz und es ist voll von Wiederholungen die eigentlich in eine Schleife gehören. Insbesondere wenn es auch auf andere Längen als sechs parametrisierbar sein soll.

In Python wird per Konvention um vier Leerzeichen pro Ebene eingerückt. Es gibt die Wahrheitswerte `True` und `False` die man anstelle von 1 und 0 verwenden sollte. Bei `isdigit()` sind dann auch das ``if`` und die beiden expliziten ``return``-Anweisungen unnötig, weil die Bedingung selbst ja schon zu einem Wahrheitswert ausgewertet wird. Den kann man auch einfach zurück geben. Ausserdem sind die Klammern um die Bedingung insgesamt als auch um die beiden Teilausdrücke überflüssig. Das ``and`` kann man sich an der Stelle auch sparen, weil man Vergleichsoperatoren verketten kann. Übrig bleiben würde dann ``return '0' <= value <= '9'``. Aber man könnte auch einfach die `isdigit()`-Methode auf Zeichenketten aufrufen und sich die eigene Funktion damit komplett sparen.
problembär

Samstag 17. März 2012, 22:19

Code: Alles auswählen

#!/usr/bin/env python
# coding: iso-8859-1

s = "ABC156458ABC01234567ABCABC431747ABC61"
a = ""
x = 0
for i in range(len(s)):
    if s[i].isdigit():
        a += s[i]
        x += 1
        if x == 6:
            if i == len(s) - 1 or not s[i + 1].isdigit():
                print "Hit: " + a
                a = ""
                x = 0
    else:
        a = ""
        x = 0
Aber die RegEx ist schon recht cool.
lunar

Samstag 17. März 2012, 22:30

@SPUTNiK: Wenn unbedingt ohne reguläre Ausdrücke, dann vielleicht so:

Code: Alles auswählen

def find_numbers(s, digits):
    groups = (s[i:i+digits] for i in xrange(len(s)))
    return [g for g in groups if len(g) == digits and g.isdigit()]
Aufzurufen wie folgt:

Code: Alles auswählen

In [8]: s = 'ABC156458ABC01234567ABCABC431747ABC61'

In [12]: find_numbers(s, 6)
Out[12]: [u'156458', u'012345', u'123456', u'234567', u'431747']
Es mag andere, elegantere Lösungen geben. Problembärs Lösung ist allerdings keine gute Lösung, sondern C in Python-Syntax.
BlackJack

Samstag 17. März 2012, 22:43

@EyDu, @lunar: Euer Code findet überlappende Zifferngruppen, das Original nicht, weil dort gefundene Ziffern durch '.' ersetzt werden und damit nicht mehr Teil weiterer Treffer sein können.
EyDu
User
Beiträge: 4872
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Samstag 17. März 2012, 22:51

Ehrlich gesagt, habe ich mir nicht angeschaut, was genau oben rauskommen soll. Ich bin einfach vom allgemeinsten Fall ausgegangen. Ohne Überlappungen ist es ja nur noch einfacher.
Das Leben ist wie ein Tennisball.
lunar

Samstag 17. März 2012, 22:59

@BlackJack: Und ich habe mir wiederum nur cofis Ausdruck angesehen, ohne das Original weiter zu beachten, es war mir viel zu konfus. ;)

@cofi: Die Lösung ohne reguläre Ausdrücke wird komplizierter, da man bei Ausschluss überlappender Ziffernfolgen die Ausdehnung der letzten Ziffernfolge berücksichtigen muss. Ergo hat man Zustände und kann nicht mehr so schöne geschlossene Ausdrücke aufstellen. Zumindest fällt mir keine Lösung ein, die nur mit Generatorausdrücken, LCs und itertools auskäme…
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Samstag 17. März 2012, 23:09

@lunar: Ich bin unschuldig, das hat EyDu alleine verbrochen :roll:
lunar

Samstag 17. März 2012, 23:22

@cofi: Oh, das tut mir leid. Keine Ahnung, wie ich auf Dich kam, ich hoffe, Du siehst mir diesen Fauxpas nach :oops:
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Samstag 17. März 2012, 23:23

@lunar: Keine Sache ;)
Benutzeravatar
SPUTNiK
User
Beiträge: 3
Registriert: Samstag 17. März 2012, 19:58

Dienstag 20. März 2012, 16:33

Uff, vielen Dank für die zahlreichen Antworten! Wie man sieht bin ich blutiger Anfänger und darf mir nun erstmal alles zu Gemüte führen und verinnerlichen. 

Das die isdigit() Methode existiert ist mir bekannt. Die fehlerhafte Funktion ist beim herumexperimentieren entstanden und im Script verblieben, wo sie letztendlich doch für etwas gut war ;)

Btw. Ich habe bei dem Script von problembär ein kleines Verständnisproblem. Wie genau wird die 12. Zeile interpretiert? Der zweite Teil ist mir schlüssig,, doch was hat es mit dem ersten auf sich?

LG Jack
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Dienstag 20. März 2012, 17:23

SPUTNiK hat geschrieben:Btw. Ich habe bei dem Script von problembär ein kleines Verständnisproblem. Wie genau wird die 12. Zeile interpretiert? Der zweite Teil ist mir schlüssig,, doch was hat es mit dem ersten auf sich?
Solltest du tatsächlich das ``if i == len(s) - 1`` meinen: Da i in der Schleife dem aktuellen Index entspricht und len(s) - 1 der letzte mögliche Index ist, wird mit diesem Vergleich lediglich überprüft ob das Ende erreicht wurde. Ehrlich gesagt fand ich ihn verständlicher als den zweiten Teil der Bedingung. Aber gut, wirklich verständlich ist es sowieso nicht in dem Stil Python-Programmierung zu betreiben.
Benutzeravatar
SPUTNiK
User
Beiträge: 3
Registriert: Samstag 17. März 2012, 19:58

Dienstag 20. März 2012, 23:03

Ohje, das -1 hat mich die ganze Zeit ziemlich irritiert. Ich dachte len(s)-1 (in dem Fall 28) wäre der zweitletzte Index da der string aus 29 Zeichen besteht und print len(s) ebenfalls 29 ausgibt. Das hat bei mir irgendwie verdrängt das der erste Index 0 ist und nicht 1 *facepalm. :mrgreen:
problembär

Mittwoch 21. März 2012, 01:04

SPUTNiK hat geschrieben:Ohje, das -1 hat mich die ganze Zeit ziemlich irritiert. Ich dachte len(s)-1 (in dem Fall 28) wäre der zweitletzte Index da der string aus 29 Zeichen besteht und print len(s) ebenfalls 29 ausgibt. Das hat bei mir irgendwie verdrängt das der erste Index 0 ist und nicht 1 *facepalm. :mrgreen:
Daß der Index von Listen (oder Arrays) bei 0 und nicht bei 1 beginnt, ist seit vielen Jahren eines der Hauptschwierigkeiten für Programmierer, verursacht oft Fehler, und es wird für viele Sprachen diskutiert, ob das so beibehalten werden soll (siehe z.B. Google zu "array index start"). Man hat es aber trotzdem nicht geändert und wird es daher wohl auch nicht mehr.

Code: Alles auswählen

if i == len(s) - 1 or not s[i + 1].isdigit():
Mein Code prüft ja auf 6 Zahlen nacheinander, und dann, ob das Zeichen danach auch eine Zahl ist oder nicht. Wenn man am Ende des Strings ist, ist das Zeichen danach sicher keine Zahl mehr, aber es existiert auch gar nicht, daß heißt, es darf dann auch nicht zu der Überprüfung dieses nichtexistenten Zeichens kommen, sonst würde es einen Fehler geben ("list index out of range"). Daher "if i == len(s) - 1" zuerst. Wenn dies "True" ist, wird der Code-Block darunter ausgeführt, ohne daß der Teil hinter dem "or ..." noch geprüft wird (wichtig, sonst wie gesagt Error).
"s[i + 1].isdigit()" gibt "True" zurück, wenn das nachfolgende Zeichen eine Ganzzahl ist, sonst "False".
Man könnte also auch

Code: Alles auswählen

if ... s[i + 1].isdigit() == False:
schreiben,

Code: Alles auswählen

if ... not s[i + 1].isdigit():
ist eine Verkürzung, die - in diesem Fall - dasselbe macht (es gibt dazu ein paar Einzelheiten, dazu siehe bei Interesse die Diskussion hier).

Wenn man in einer if-Bedingung einen Ausdruck hat, kann man den einfach so stehen lassen und auf die Auswertung zu "wahr" oder "falsch" testen, siehe hier.
Z.B. wird eine ganze Zahl wie 5 zu "wahr" ausgewertet. Man kann dann also schreiben:

Code: Alles auswählen

if 5:
    print "Ausdruck wahr"
Ebenso, wenn eine Funktion einen solchen Wert wie "True" zurückgibt.

Code: Alles auswählen

if "5".isdigit():
    print 'Der String "5" enthält eine Ganzzahl.'
umgekehrt

Code: Alles auswählen

if not "a".isdigit():
    print 'Der String "a" ist keine Ganzzahl.'
Hoffe, die Zeile in dem Skript wird Dir so ein bißchen verständlicher.

So, und da die anderen Leute hier meine Erklärungen normalerweise nicht leiden können, bin ich jetzt raus hier, SPUTNiK weitere Fragen an mich bitte ggf. per PN.
Antworten