Telefonnummern ungültige Zeichen entfernen

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.
Antworten
Dragonito
User
Beiträge: 19
Registriert: Mittwoch 22. März 2006, 07:31
Wohnort: Bonn
Kontaktdaten:

Hallo Leute,

ich ziehe mir aus einer CSV-Datei Kontakte heraus. Diese enthalten unter anderem Telefonnummern. Jetzt möchte ich die Nummern vereinheitlichen. Bspw. alle Leerzeichen rausziehen etc. einige eingaben waren bspw auch mit nem Punkt oder auch Simikolon.

Jetzt zu meiner Frage, wie kann ich am sinnvollsten alle nicht numerischen Zeichen aus einem String entfernen?


Tschaui

Drago

PS: Bin noch recht frisch was Python angeht, also habt erbarmen ;-)
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Dragonito hat geschrieben:Jetzt zu meiner Frage, wie kann ich am sinnvollsten alle nicht numerischen Zeichen aus einem String entfernen?
Hi Drago!

Es gibt mehrere Möglichkeiten. Suche dir das raus was du brauchst oder kombiniere die Möglichkeiten.

Code: Alles auswählen

>>> tel = "05262/123 123 123 - 15"
>>> print tel.replace("/", "")
05262123 123 123 - 15
>>> print tel.replace(" ", "")
05262/123123123-15
>>> import string
>>> print string.digits
0123456789
>>> print "".join([ char for char in tel if char in string.digits ])
0526212312312315
>>> 
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Dragonito
User
Beiträge: 19
Registriert: Mittwoch 22. März 2006, 07:31
Wohnort: Bonn
Kontaktdaten:

Hi Gerold,

die Säuberung des Strings klappt mittlerweile prima, danke für die Tipps.

Bei der Verarbeitung sind mir allerdings folgende Probleme aufgefallen. Ich habe ein Feld Vorwahl und ein Feld Telefonnummer. Jetzt ist nicht in jedem Feld die Vorwahl gefüllt sondern es kommt auch vor das im Feld Telefonnummern bspw. Die Vorwahl+Telefonnummer+Durchwahl drin hängen. Mal sind diese Daten mit "/" getrennt und dann wieder mit "-". Die Form "vorwahl/telefonnummer" bzw. "vorwahl-telefonnummer" lies sich relativ einfach splitten. Problem ist, ich weiss nicht wann die Nummer genau in den genannten Formaten auftreten. Problematisch gestalltet sich derzeit folgendes Szenario:

vorwahl-telefonnummer-durchwahl.

So wenn ich das splitte bekomme ich nen Tupel mit 3 Inhalten wieder. Meine Idee war es die Anzahl der Tupel zu zählen und dann zu sagen Tupel[0] ist die Vorwahl wenn tupelanzahl > 2. In meinem schlauen Buch habe ich bereits nach einer Funktion gesucht, welche mir die Anzahl der enthaltenen Felder eines Tupels ausgibt, leider bisher ohne erfolg :(

Wenn das geschafft ist muss ich mir Gedanken machen, wie ich vorgehen soll, wenn das Feld Vorwahl gefüllt ist aber auch das Feld Telefonnummer und dort auch die Vorwahl enthalten ist, hier muss ich im Prinzip gegenprüfen.

Warum können User nicht die Nummer immer richtig eintippen? *schlurchts* ;-)

Tschau

Drago
rumilmirion
User
Beiträge: 34
Registriert: Mittwoch 3. Mai 2006, 12:09

Dragonito hat geschrieben: Meine Idee war es die Anzahl der Tupel zu zählen
Die Anzahl der Elemente in einer Tupel (und generell) bekommt man über len heraus:

Code: Alles auswählen

>>>string='1234-5678-90'
>>>tupel=string.split('-')
>>>len(tupel)
3
Zuletzt geändert von rumilmirion am Freitag 19. Mai 2006, 11:23, insgesamt 2-mal geändert.
dev
User
Beiträge: 49
Registriert: Montag 23. Januar 2006, 09:52
Kontaktdaten:

Warum können User nicht die Nummer immer richtig eintippen?
Du beantwortest Deine Frage ja schon selbst - es sind halt User. *scnr*

Und die Länge eines Tupels bestimmst Du ganz einfach mit:

Code: Alles auswählen

>>> t = ( 1, 2, 3)
>>> len(t)
3
>>>
Aber bezüglich Telefonnummern erkennen schau Dir doch mal Case study: Parsing Phone Numbers

Vielleicht bringt Dich das ja auf Ideen wie Du das bei Dir lösen kannst.

Ciao,
dev
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Dragonito hat geschrieben: Bei der Verarbeitung sind mir allerdings folgende Probleme aufgefallen. Ich habe ein Feld Vorwahl und ein Feld Telefonnummer. Jetzt ist nicht in jedem Feld die Vorwahl gefüllt sondern es kommt auch vor das im Feld Telefonnummern bspw. Die Vorwahl+Telefonnummer+Durchwahl drin hängen. Mal sind diese Daten mit "/" getrennt und dann wieder mit "-". Die Form "vorwahl/telefonnummer" bzw. "vorwahl-telefonnummer" lies sich relativ einfach splitten. Problem ist, ich weiss nicht wann die Nummer genau in den genannten Formaten auftreten.
Drago
Hi Dragonito,

erstmal muss ich sagen, dass ich staune ich, in welchem Tempo hier Antworten auf neue Themen folgen. Ich hoffe, mein Beitrag hier ist nicht schon überflüssig.

Zurück zum Thema: sofort als ich das Problem las, viel mir mein Lieblingsgebiet ein: Regular Expressions. Die sind für diese Art von Problemen predestiniert, da sie quasi maßgeschneidert werden können. Ohne viele Worte hier mal mein Lösungsvorschlag:

Code: Alles auswählen

import re, string

##  erstelle kompiliertes re-patterns
creSegmentpattern = re.compile(r"((?P<vorwahl>\d+)[/ -])?(?P<nummer>.+?)([- ](?P<durchwahl>\d*))?$")
creZahlpattern = re.compile(r"\d+")

def teile_nummer(sTel):
    oResult = creSegmentpattern.match(sTel)
    if oResult:
        dResult = oResult.groupdict()
        dResult["nummer"] = reduce(string.join, creZahlpattern.findall(dResult.get("nummer")))
    else:
        dResult = {}
        print "Telefonnummer mit unzulaessigen Zeichen oder Struktur eingegeben:", sTel
    return dResult

print "Geteilte Nummer 1:", teile_nummer("05262/123 123 123 - 15")
print "Geteilte Nummer 2:", teile_nummer("05262/123123123-150")
print "Geteilte Nummer 3:", teile_nummer("05262 12312312315")
print "Geteilte Nummer 4:", teile_nummer("05262-123123123 15")
print "Geteilte Nummer 5:", teile_nummer("0526212312312315")
Ausgabe hat geschrieben:Geteilte Nummer 1: {'durchwahl': '15', 'vorwahl': '05262', 'nummer': '123123123'}
Geteilte Nummer 2: {'durchwahl': '150', 'vorwahl': '05262', 'nummer': '123123123'}
Geteilte Nummer 3: {'durchwahl': None, 'vorwahl': '05262', 'nummer': '12312312315'}
Geteilte Nummer 4: {'durchwahl': '15', 'vorwahl': '05262', 'nummer': '123123123'}
Geteilte Nummer 5: {'durchwahl': None, 'vorwahl': None, 'nummer': '0526212312312315'}
Was macht das Programm?
Einfach ausgedrückt: es beginnt vorn und erfasst die erste zusammenhängende Zahl bis zum ersten Leerzeichen, Minus oder Schrägstrich. Sind beide Bedingungen (Zahl und Endzeichen) erfüllt, wird diese Zahl dem Dictionaryschlüssel "vorwahl" zugeordnet. Dann werden alle weiteren Zeichen zusammengefasst und dem Schlüssel "nummer" zugewiesen, so dass am Ende eine Zahl übrig bleibt, die durch ein Leer- oder Minuszeichen von der Hauptnummer getrennt ist (wenn das möglich ist). Diese letzte Zahl wird mit dem Schlüssel "durchwahl" gespeichert. Zum Schluss werden die Sonderzeichen aus "nummer" entfernt.

Wie läuft das ab?
Als erstes werden die RegularExpression Muster kompiliert. Das ist eigentlich nur nötig, wenn man sehr viele Vergleiche damit durchführt, aber da ich nicht weiß, wie viele Einträge Du prüfst, ging ich lieber auf Nummer sicher.
Innerhalb der Funktion wird der als Parameter übergebene String vom Anfang an (match) nach einer Übereinstimmung für das beschriebene Muster durchsucht. Wird ein Muster gefunden, so wird ein Dictionary mit den Teilen zurückgegeben. Das Feld "nummer" kann Leer- und Minuszeichen enthalten. Deshalb werden über die zweite RegularExpression Funktion alle Vorkommen von Zahlen gesucht, die dann durch reduce wieder zu einem String zusammengesetzt werden.

Der verwendete RE Segmentpattern schrittweise erläutert:
r" - definiert als raw-string um sich mehrfache \ zu sparen
( - vorwahlsegment und Trennzeichen in Gruppe zusammenfassen
(?P<vorwahl> - Gruppe mit Label vorwahl definieren, die später über das dictionary abgefragt wird
\d+) - so viele aufeinander folgende Zahlen aufnehmen wie kommen (min. 1), dann Gruppenende
[/ -] - nach der Vorwahl muss ein /, leerzeichen oder - folgen
)? - Gruppe aus Vorwahl und Trennzeichen beendet, ? bedeutet, dass sie einmal oder keinmal vorkommt
(?P<nummer> - Gruppe mit Label nummer definieren
.+? - ein oder mehr beliebige Zeichen erlaubt, ? hinter + bedeutet, dass so wenig wie möglich Zeichen genommen werden
) - Ende der Gruppe nummer
([- ] - Beginn der letzten Gruppe, die mit Minus oder Leerzeichen beginnen muss
(?P<durchwahl> - Gruppe mit Label durchwahl definieren
\d*) - so viele zusammenhängende Zahlen wie möglich aufnehmen
)? - die Durchwahlgruppe kann einmal oder keinmal vorkommen
$" - der zu durchsuchende String muss nach der Durchwahlgruppe zuende sein

Alle Klarheiten beseitigt? ;-)

Grüße,
der Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Dragonito
User
Beiträge: 19
Registriert: Mittwoch 22. März 2006, 07:31
Wohnort: Bonn
Kontaktdaten:

Hi Michel,

danke auch für Deine Antwort. Reguläre Ausdrücke sind mir auch direkt in den Kopf gekommen, habe ich letztendlich nur zum Teil eingesetzt um die Nummern zu "säubern".

Leider sind die Daten, die ich aus der Datenbank bekomme alles andere als Normalisiert. Ich habe zum einen ein Feld Vorwahl, da steht mal was drin, mal steht nix drin. Als Zweites gibt es ein Feld Telefonnummer. Dort steht mal die Nummer ohne Vorwahl drin, mal mit Vorwahl, soweit so gut. Leider gibt es auch die Situation das die Vorwahl ins Vorwahlfeld eingetragen wurde und auch im Feld Telefonnummer die Vorwahl mit drin steht.

Ich bin also hingegangen, habe eine Funktion gebaut, welche das Vorwahlfeld aufnimmt und auch die Telefonnummer. Jetzt prüfe ich als erstes ob eine Vorwahl vorhanden ist. Diese wird sofern vorhanden zwischengespeichert. Als nächstes Prüfe ich ob in der Telefonnummer ggf. die Vorwahl auch vorhanden ist, ist dies der Fall so schneide ich diese ab. Zum Schluss gebe ich die Komplette nummer als Feld[0] Feld[1] zurück... Zwischenzeitlich wird aus den Nummern alles was nicht in eine Telefonnummer gehört rausgefiltert...

Aber selbt dieser Ablauf hilft bei 35000 Adressen nicht viel weiter, ich habe noch Fälle die dann noch die Landesvorwahl drin haben, da habe ich dann aber blockiert solche Datensätze markiere ich als "zu Bearbeiten"... soll der User sich damit rumschlagen ;) Aber ich denke das meiste konnte ich "säubern" immer noch besser als alle Daten neu zu schreiben.

Tschaui

Dragonito
hellboy
User
Beiträge: 6
Registriert: Montag 10. Januar 2011, 20:33

Michael Schneider,

ich habe Ihre Quellcode angepasst:

Code: Alles auswählen

creSegmentpattern = re.compile(r"((?P<vorwahl>[\+()\d]+)[/ -])?(?P<nummer>.*?)([- ](?P<durchwahl>\d*))?$")
creZahlpattern=re.compile(r"[\s\d-]+")
def teile_nummer(sTel):
	try:
		oResult = creSegmentpattern.match(sTel)
		dResult = oResult.groupdict()
		dResult["nummer"] = reduce(string.join, creZahlpattern.findall(dResult.get("nummer")))
	except:
		dResult = {}
		print "Telefonnummer mit unzulaessigen Zeichen oder Struktur eingegeben:", sTel
	return dResult
TODOs:
1) Ländervorvahle werden nicht immer korrekt erkannt
2) Unit-Test
hellboy
User
Beiträge: 6
Registriert: Montag 10. Januar 2011, 20:33

Es gibt auch Möglichkeit libphonenumber zu nutzen

Um Java aus Python benutzen zu können, müssen wir JCC installieren:

Code: Alles auswählen

svn co http://svn.apache.org/repos/asf/lucene/pylucene/trunk/jcc jcc
cd jcc
in setup.py JAVAC = java-6-sun-1.6.0.22 anpassen

Code: Alles auswählen

python setup.py build
sudo python setup.py install
wget http://libphonenumber.googlecode.com/files/libphonenumber-2.5.1.jar
sudo python -m jcc.__main__ --debug --shared --jar libphonenumber-2.5.1.jar --classpath /home/... --python libphonenumber --install

Code: Alles auswählen

ipython

from my import initVM, CLASSPATH, PhoneNumberUtil
initVM(classpath=CLASSPATH)
pnu = PhoneNumberUtil.getInstance()
pn = pnu.parse(tel, "DE")
fmt = pnu.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL)
Zuletzt geändert von hellboy am Dienstag 15. Februar 2011, 14:59, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

hellboy hat geschrieben: Um Java aus Python benutzen zu können, müssen wir ...
Oder Jython nutzen ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@hellboy
Sag mal ist dir schon aufgefallen das der Thread, nun ja letzter Eintrag Ende Mai 2006, tot ist? :roll:
Warum hast du nicht ein neuen Thread aufgemacht und dann hierher verlinkt, das dich dieser Thread dazu inspiriert hatte das Thema nochmal anzugehen.

Ich meine ja nur mal Java 6 ist erst im Dezember 2006 rausgekommen und ich denke das Jython nun schon um einiges weiter als JCC ist, oder ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
hellboy
User
Beiträge: 6
Registriert: Montag 10. Januar 2011, 20:33

es geht um a Auffindbarkeit des Threads 8)

ich hatte dieselbe Probleme und Thread gefunden mit Schlüsselwort "Vorwahl". Es geht hier auch um
die Nummern vereinheitlichen Bspw. alle Leerzeichen rausziehen etc. einige eingaben waren bspw auch mit nem Punkt oder auch Simikolon.
hellboy
User
Beiträge: 6
Registriert: Montag 10. Januar 2011, 20:33

Hyperion hat geschrieben:
hellboy hat geschrieben: Um Java aus Python benutzen zu können, müssen wir ...
Oder Jython nutzen ;-)
und was ist in diesem Fall besser?

Jithon vs JCC

:wink:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

hellboy hat geschrieben: und was ist in diesem Fall besser?

Jithon vs JCC

:wink:
Kommt drauf an. Wenn man eh Jython nutzt, dann erübrigt sich die Frage. Wenn nein, würde ich keines von beiden nehmen, sondern diese - augenscheinlich recht einfache Sache - selber in Python implementieren ;-)

Genau das war ja wohl auch mal Sinn des Threads. So schwer sieht mir das jetzt nicht aus - evtl. verkenne ich jetzt aber auch die Komplexität, die Telefonnummern annehmen können.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

Bevor man sich irgendwie Java ans Bein bindet, könnte man auch einfach die Python-Portierung von der Bibliothek verwenden: https://github.com/tereno/libphonenumber-Python

@Hyperion:
libphonenumber -- Highlights of functionality hat geschrieben:
  • Parsing phone numbers for 228 countries/regions, and formatting/validating phone numbers for 196 countries/regions of the world.
  • getNumberType - gets the type of the number based on the number itself; able to distinguish Fixed-line, Mobile, Toll-free, Premium Rate, Shared Cost, Voip and Personal Numbers (whenever feasible).
  • isNumberMatch - gets a confidence level on whether two numbers could be the same.
hellboy
User
Beiträge: 6
Registriert: Montag 10. Januar 2011, 20:33

@BlackJack

selbstverständlich hab ich diese Möglichkeit berücksichtigt und hab versucht auszuprobieren - leider erfolglos.
IMHO Port ist recht jung und in diesem Fall Java-Lib zu nutzen ist einfacher.

:K
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

hellboy hat geschrieben:@BlackJack

selbstverständlich hab ich diese Möglichkeit berücksichtigt und hab versucht auszuprobieren - leider erfolglos.
IMHO Port ist recht jung und in diesem Fall Java-Lib zu nutzen ist einfacher.
:K
Wenn man JVM installiert hat oder installieren kann. Wenn C-Python installiert ist, dann ist der Umstieg auf Jython, nur um eine Java-Implementierung zu verwenden, für dieses triviale Problem nun wirklich von hinten durch die Brust ins Auge.

Gruß,
Micha
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

BlackJack hat geschrieben: @Hyperion:
libphonenumber -- Highlights of functionality hat geschrieben:
  • Parsing phone numbers for 228 countries/regions, and formatting/validating phone numbers for 196 countries/regions of the world.
  • getNumberType - gets the type of the number based on the number itself; able to distinguish Fixed-line, Mobile, Toll-free, Premium Rate, Shared Cost, Voip and Personal Numbers (whenever feasible).
  • isNumberMatch - gets a confidence level on whether two numbers could be the same.
Wirkt etwas beschränkt, meinst Du nicht? Dann doch lieber die RegEx. :wink:
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Antworten