Reguläre Ausdrücke C#

batch_prog

Cadet 2nd Year
Registriert
März 2017
Beiträge
27
Ich versuche mit nachfolgendem Skript Werte (Straße von der Hausnummer) zu trennen, allerdings bekomme ich noch nicht ganz das gewünschte Ergebniss.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
using System.IO;
using System.Threading;
using System.Text.RegularExpressions;

namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string[] tests = new string[] {
"1 Teststreet",
"1 a Teststreet",
"1a Teststreet",
"Teststreet 1",
"Teststreet 1 a",
"Teststreet 1a",
"Teststreet 1 a-f",
"Teststreet 1a-f",
"Teststreet 1-400 ss-ss",
"1 a-f Teststreet",
"1a-f Teststreet",
"67 a-g Musterstraße",
"67a-g Muster straße",
"Ernst-Thäl-mann-stra-sse 8",
"203 East 50th St.",
"12th Street 123",
"Musterstraße 67 a-g",
"Musterstraße 67a-g",
"D1 4-8",
"Johann-G.-Gutenbergstr 22 A",
"Johann - G. - Gutenbergstr 22 A",
"King George V 3a(1)",
"3a(1) King George V",
};

string result = string.Join(Environment.NewLine, tests
.Select(test => new {
raw = test,
address = SplitStreet(test)
})
.Select(test => $"{test.raw,25} -> {test.address.street} :: {test.address.number}"));

Console.Write(result);
Console.ReadKey();
}

private static (string street, string number) SplitStreet(string value)
{
int count = value.Count(f => f == '-');
string pattern="";
for (int i = 0; i < count; i++)
{
pattern = pattern + @"\p{P}{0,}\p{L}{1,}";
}
var match = Regex.Match(value, @"\p{N}{0,}\p{L}{2,}" + @pattern + @"\p{N}{0,}(?:\s\p{L}+)*");

string street = match.Value;

string number = (match.Index > 0)
? value.Substring(0, match.Index)
: value.Substring(match.Index + match.Length);

return (street, number.Trim());
}
}
}

Leider passt es noch nicht ganz bei den Straßen: Johann - G. - Gutenbergstr 22 A, D1 4-8, 203 East 50th St. , Johann-G.-Gutenbergstr. 22 A

Kann mir da jemand von Euch helfen?
 
Zuletzt bearbeitet:
Magst du dir mal deinen Beitrag erstmal ansehen und den Code bitte in die davorgesehen Code Tags packen? So ist es unlesbar bzw. muehsam. Ist ja nicht dein erster Beitrag in diesem Unterforum

Man kann die regulaeren Ausdruecke zudem auch erstmal unabhaengig vom Code ausprobieren und zusammenbasteln, gibt tolle Online Tools wie Regex101. Das verhindert zumindest moegliche Code Probleme.
 
  • Gefällt mir
Reaktionen: madmax2010, batch_prog und Hayda Ministral
Ich habe es einmal angepasst.

Mein bisheriger Output, weißt Du was ich anpassen muss, damit es bei allen Adressen passt?:

1 Teststreet -> Teststreet :: 1

1 a Teststreet -> Teststreet :: 1 a

1a Teststreet -> Teststreet :: 1a

Teststreet 1 -> Teststreet :: 1

Teststreet 1 a -> Teststreet :: 1 a

Teststreet 1a -> Teststreet :: 1a

Teststreet 1 a-f -> Teststreet :: 1 a-f

Teststreet 1a-f -> Teststreet :: 1a-f

Teststreet 1-400 ss-ss -> Teststreet :: 1-400 ss-ss

1 a-f Teststreet -> Teststreet :: 1 a-f

1a-f Teststreet -> Teststreet :: 1a-f

67 a-g Musterstraße -> Musterstraße :: 67 a-g

67a-g Muster straße -> Muster straße :: 67a-g

Ernst-Thäl-mann-stra-sse 8 -> Ernst-Thäl-mann-stra-sse :: 8

203 East 50th St. -> East :: 203

12th Street 123 -> 12th Street :: 123

Musterstraße 67 a-g -> Musterstraße :: 67 a-g

Musterstraße 67a-g -> Musterstraße :: 67a-g

D1 4-8 -> :: D1 4-8

Johann-G.-Gutenbergstr 22 A -> Johann-G.-Gutenbergstr :: 22 A

Johann - G. - Gutenbergstr 22 A -> Johann :: - G. - Gutenbergstr 22 A

Johann-G.-Gutenbergstraße 22 A -> Johann-G.-Gutenbergstraße :: 22 A

Johann-G.-Gutenbergstr. 22 A -> Johann-G.-Gutenbergstr :: . 22 A

King George V 3a(1) -> King George V :: 3a(1)

3a(1) King George V -> King George V :: 3a(1)
 
Kannst du denn für deine Beispiele eine Regel in deutscher Sprache verfassen, die allgemein gültig ist?

Ich glaube hier gibt es keine Eindeutigkeit, daher wäre das meiner Auffassung nach rein regelbasiert nicht möglich jeden Fall korrekt zu bestimmen.
 
  • Gefällt mir
Reaktionen: batch_prog und KataLiS
203 East 50th St. -> East :: 203
D1 4-8 -> :: D1 4-8
Kann mir da jemand von Euch helfen?
Nicht wirklich. Das sieht nicht nach einem Regex Problem, sondern einem Problem für höhere Künstliche Intelligenz aus - ich bin mir selbst auch nicht sicher was bei "D1 4-8" Straße und was Hausnummer sein sollte. Distrikt 1 Haus 4 bis 8?
Das Problem ist, dass das scheinbar ein 1-Algorithmus-für-alles sein soll. Wenn man die Adressen in deutsche und amerikanische aufteilt anstatt alles mit einem Regex zu lösen wäre es einfacher.

Bagbag war schneller.
 
  • Gefällt mir
Reaktionen: batch_prog
KataLiS schrieb:
Nicht wirklich. Das sieht nicht nach einem Regex Problem, sondern einem Problem für höhere Künstliche Intelligenz aus - ich bin mir selbst auch nicht sicher was bei "D1 4-8" Straße und was Hausnummer sein sollte. Distrikt 1 Haus 4 bis 8?
Das Problem ist, dass das scheinbar ein 1-Algorithmus-für-alles sein soll. Wenn man die Adressen in deutsche und amerikanische aufteilt anstatt alles mit einem Regex zu lösen wäre es einfacher.

Bagbag war schneller.

Bei D1 4-8 handelt es sich um eine Straße in Mannheim. D1 ist das Quadrat und 4-8 die Nummer. Wenn ich Euch richtig verstehe, kann der Fall nicht mit eingebaut werden?

Wie sieht es denn aus, wenn die Adressen aufgeteilt werden?
 
batch_prog schrieb:
kann der Fall nicht mit eingebaut werden?
Das Problem hier ist, dass die Hausnummer zum Beispiel manchmal Buchstaben und Zahlen enthält, aber wie das D1 Beispiel zeigt gibt es in der Straße auch Fälle mit Zahlen. Die Kürze der Hausnummer ist, wie D1 zeigt, auch kein eindeutiger Indikator. Woher soll man nun Wissen, ob der Textbaustein eine Straße, oder eine Hausnummer ist?

Wie bagbag vorschlug kann es helfen das Problem erst in Textform zu lösen (z.B. eine Hausnummer befolgt immer folgende Regeln und unterscheidet sich immer von der Straße durch diese Regeln), bevor man die Computer-Code Lösung schreibt.

Das ist nur als Programmierübung, richtig? Ich vermute, dass Postunternehmen das Problem so lösen, dass sie eine Datenbank mit allen Straßennamen in der Stadt durchforsten und dann das Ergebnis nehmen, dass dem angegebenen Namen am nächsten kommen und dann hoffen, dass der Zustell-Fahrer den Rest erledigt.
 
Zuletzt bearbeitet:
Problem ist hier, dass in jedem Land (oder Stadt) die Straßen sich anders zusammen setzen. Daher kannst du eigentlich keine allgemein gültige Regel festlegen. Teilweise wissen ja nicht mal die Menschen, wie sich die Straße zusammen setzt (siehe Mannheim)
Du könntest höchstens versuchen erst herauszufinden, wo diese Straße sich befindet(Stadt/Land) und dann dementsprechend für jedes Muster eine eigene Regel schreiben.
 
KataLiS schrieb:
Das ist nur als Programmierübung, richtig? Ich vermute, dass Postunternehmen das Problem so lösen, dass sie eine Datenbank mit allen Straßennamen in der Stadt durchforsten und dann das Ergebnis nehmen,
Die haben Adressdaten von Dienstleistern, ähnlich wie libpostal funktioniert.
 
  • Gefällt mir
Reaktionen: BeBur
So ein vielfältiges Thema würde ich nicht mit Regex angehen, jedenfalls nicht so wie du das da machst. Falls das produktiv genutzt werden soll wird das nen Haufen Bugs generieren.
Im Mindestens solltest du die Regex sinnvoll benennen und einzeln Testen und erst im Zweiten Schritt iterativ matchen. Das auch, wenn es eine Hausaufgabe ist.

Du kannst aber davon ausgehen, dass alle seriösen Anbieter/Libs (inklusive libpostal) eine große Datenbank mit realen Straßennamen (und Hausnummern) haben und diese als Ausgangsbasis hernehmen.

Die reale Lösung für dein Problem besteht natürlich darin, die Felder "Adresse" und "Hausnummer" zu trennen.
 
Das was du hier machen musst (egal ob mit RegEx oder einem eigenen Parser), ist, dir erst mal bewusst zu werden, was du da an Daten genau vor dir hast.
Das geht in diesem Fall relativ einfach, indem du Stichproben, von einfach bis möglichst komplex und einzigartig, heraus nimmst und händisch in Straße und Hausnummer aufdröselst.
Daran anschließend ordnest du dir die Ergebnisse der Komplexität nach aufsteigend, damit du gut zwischen Regel- und Grenzfälle unterscheiden kannst.
Danach kannst du zu Beginn die Regeln für die Regelfälle aufstellen und diese dann nach und nach um die komplexer werdenden Fälle erweitern.
Fallen dir währenddessen Sonderlinge auf, die kaum in den bisherigen Rahmen passen, verschieb sie in der Liste nach hinten, bzw. merk sie dir für einen späteren Zeitpunkt vor.

Je nachdem, wie stark sich die Regeln für Straße und Hausnummer dann am Ende annähern, kann es einen Punkt geben, bei dem die mögliche Genauigkeit, durch erweitern der Regeln, nicht mehr erhöht werden kann, da Regelerweiterungen nicht mehr nur noch die Trefferrate erhöhen, sondern auch die False-Positives.
(Spoiler: Für die von dir genannten Fälle geht das jedoch relativ gut allein mit RegEx.)
Ab diesem Punkt kämst du allein mit RegEx nicht mehr weiter, da weitere Werkzeuge herangezogen werden müssten. (Das könnten eine oder mehrere, weitere Regular Expressions sein, die entweder auf den kompletten Input oder/und auf einzelne Teile angewendet werden, spezifische Heuristiken, die mit geeigneten Mitteln den Input weiter analysieren, das Heranziehen einer bestehenden Datenbank mit Sonderfällen oder/und Regeln für Sonderfälle, oder aber User-Input, entweder direkt oder per Eintrag in eine Datenbank für aufzulösende Konfliktfälle, u.v.m.…)

Ich habe keine tiefgehenden Erfahrungen mit Adressen, aber für deine Fälle hast du den größten Erfolg, wenn du dich auf die Hausnummer konzentrierst und die Straße als "Beifang" behandelst.
Das sähe dann in etwa wie folgt aus (über die genaue Reihenfolge lässt sich streiten...):

AdresseRegEx
Meinestraße 1sehr simpel: ^(.*) +(\d+)$
Johann-G.-Gutenbergstr. 22Ajetzt mit direkt folgendem, einzelnem Buchstaben als Zusatz: ^(.*) +(\d+[A-Za-z]?)$
Johann - G. - Gutenbergstr 22 AEs darf jetzt ein optionales Leerzeichen zwischen Hausnummer und Zusatz sein
D1 4-8Auf die Hausnummer mit Zusatz darf, getrennt mit Bindestrich, eine weitere Hausnummer mit Zusatz folgen. (U.U. potentiell vorkommende Leerzeichen zwischen dem Bindestrich mitbeachten!)
203 East 50th St. Die Hausnummer darf nun auch vor der Straße stehen. Dafür einfach den Straßenteil der RegEx "(.*) +" zur optionalen Capture-Group machen und das Gleiche nochmal hinter die Hausnummer hängen (Reihenfolge bzgl. des Leerzeichens beachten!)

Immer darauf achten, dass bei jedem weiteren Schirtt die Fälle der vorherigen Schritte auch immer noch korrekt gefunden werden!
Hast du alles richtig gemacht, funktioniert das jetzt ohne Probleme für die genannten Fälle und noch ein paar mehr.
Kennst du weitere Sonderfälle oder findest du solche beim Testen in deinen Daten, musst du das Ganze entsprechend erweitern.
 
Zuletzt bearbeitet: (Rechtschreibung)
  • Gefällt mir
Reaktionen: KataLiS und BeBur
Zurück
Oben