staging.inyokaproject.org

Adressdaten extrahieren

Status: Gelöst | Ubuntu-Version: Ubuntu 22.10 (Kinetic Kudu)
Antworten |

safran

Anmeldungsdatum:
30. April 2023

Beiträge: Zähle...

Hallo liebe Ubuntuuser,

ich habe ein wichtiges Anliegen mit der Shell.

habe verschiedene Datensätze folgender Form:

"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1"",""addr:postcode""=>""73779"",""addr:street""=>""Tulpenweg"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1"",""addr:postcode""=>""73779"",""addr:street""=>""Uhlandstraße"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1"",""addr:postcode""=>""73779"",""addr:street""=>""Ulmenweg"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1"",""addr:postcode""=>""73779"",""addr:street""=>""Veilchenweg"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1"",""addr:postcode""=>""73779"",""addr:street""=>""Waldstraße"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1"",""addr:postcode""=>""73779"",""addr:street""=>""Wertstraße"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1"",""addr:postcode""=>""73779"",""addr:street""=>""Wilhelm-Busch-Weg"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1"",""addr:postcode""=>""73779"",""addr:street""=>""Wilhelmstraße"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1/1"",""addr:postcode""=>""73779"",""addr:street""=>""Ahornweg"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""1/1"",""addr:postcode""=>""73779"",""addr:street""=>""Jahnstraße"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""10"",""addr:postcode""=>""73779"",""addr:street""=>""Achalmstraße"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""10"",""addr:postcode""=>""73779"",""addr:street""=>""Altbacher Straße"""
"""addr:city""=>""Deizisau"",""addr:country""=>""DE"",""addr:housenumber""=>""10"",""addr:postcode""=>""73779"",""addr:street""=>""Alte Bergstraße"""

Da wollte ich nun die Stadt, die Hausnummer, die PLZ und den Straßennamen sauber extrahieren für eine CSV-Datei.

Wie könnte ich dafür ein Skript anfertigen, ist es mit Substringsuche, und dann nochmal nach Suche nach dem nächsten " oder geht es eleganter?

Vielen Dank

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

Schräges Format. Aber mit sed geht das:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ sed -re 's#""addr:[^"]*""=>""([^"]*)"",?#\1;#g;s#^"(.*);"#\1#' data
Deizisau;DE;1;73779;Tulpenweg
Deizisau;DE;1;73779;Uhlandstraße
Deizisau;DE;1;73779;Ulmenweg
Deizisau;DE;1;73779;Veilchenweg
Deizisau;DE;1;73779;Waldstraße
Deizisau;DE;1;73779;Wertstraße
Deizisau;DE;1;73779;Wilhelm-Busch-Weg
Deizisau;DE;1;73779;Wilhelmstraße
Deizisau;DE;1/1;73779;Ahornweg
Deizisau;DE;1/1;73779;Jahnstraße
Deizisau;DE;10;73779;Achalmstraße
Deizisau;DE;10;73779;Altbacher Straße

safran

(Themenstarter)

Anmeldungsdatum:
30. April 2023

Beiträge: 5

Hi,

super schon einmal vielen Dank,

meinst Du ich kann lernen und verstehen was diese Zeile macht?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

Und noch mit Ruby

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ ruby -r csv -ne 'puts $_.scan(/(?<==>"")(?:.*?)(?="")/).to_csv(col_sep:";")' data
Deizisau;DE;1;73779;Tulpenweg
Deizisau;DE;1;73779;Uhlandstraße
Deizisau;DE;1;73779;Ulmenweg
Deizisau;DE;1;73779;Veilchenweg
Deizisau;DE;1;73779;Waldstraße
Deizisau;DE;1;73779;Wertstraße
Deizisau;DE;1;73779;Wilhelm-Busch-Weg
Deizisau;DE;1;73779;Wilhelmstraße
Deizisau;DE;1/1;73779;Ahornweg
Deizisau;DE;1/1;73779;Jahnstraße
Deizisau;DE;10;73779;Achalmstraße
Deizisau;DE;10;73779;Altbacher Straße
Deizisau;DE;10;73779;Alte Bergstraße

shiro

Anmeldungsdatum:
20. Juli 2020

Beiträge: 611

safran schrieb:

meinst Du ich kann lernen und verstehen was diese Zeile macht?

Ja, ein bisschen lesen (z.B. "Friedl, Jeffrey - Reguläre Ausdrücke" oder "Goyvaerts, Jan - Reguläre Ausdrücke Kochbuch" usw) und ausprobieren.

Wenn dir die RegEx im Anfang etwas kryptisch vorkommt, kannst du es auch prozedural z.B. mit "awk" machen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ awk -F"," '{gsub("\"","");for(i=1;i<=NF;i++){split($i,a,"=>");printf "%s%s",a[2],(i==NF?"\n":";")}}' data
Deizisau;DE;1;73779;Tulpenweg
Deizisau;DE;1;73779;Uhlandstraße
Deizisau;DE;1;73779;Ulmenweg
Deizisau;DE;1;73779;Veilchenweg
Deizisau;DE;1;73779;Waldstraße
Deizisau;DE;1;73779;Wertstraße
Deizisau;DE;1;73779;Wilhelm-Busch-Weg
Deizisau;DE;1;73779;Wilhelmstraße
Deizisau;DE;1/1;73779;Ahornweg
Deizisau;DE;1/1;73779;Jahnstraße
...

Mit der "gsub" Funktion werden die Tüddel (") gelöscht. Alle Felder sind mit Komma (-F",") getrennt. Innerhalb eines Feldes werden Feldname und Wert durch "⇒" getrennt (split Funktion). Mit "printf" wird der Wert (a[2]) und das Trennzeichen ";" oder "\n" (beim letzten Feld) ausgegeben.

noisefloor Team-Icon

Ehemaliger
Avatar von noisefloor

Anmeldungsdatum:
6. Juni 2006

Beiträge: 28316

Hallo,

so rein Interesse halber: hast du einen Einfluss darauf, wie du die Daten bekommst? Die Datenstruktur sieht aus wie ein Unglück aus CSV, XML und Ansätzen einer Graphdatenbank.

Gruß, noisefloor

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

safran schrieb:

meinst Du ich kann lernen und verstehen was diese Zeile macht?

Ja, wieso nicht?

1
sed -re 's#""addr:[^"]*""=>""([^"]*)"",?#\1;#g;s#^"(.*);"#\1#' data

Ich versuche es mal zu erklären

  • sed

  • -r: erweiterte Regex, spart Backslashes für das Escaping z.B. bei den Klammern für die Gruppen

  • -e: das nächste ist das Sed-Programm

  • Programm besteht aus zwei Befehlen getrennt durch ";"

    • s#""addr:[^"]*""⇒""([^"]*)"",?#\1;#g - Ersetzung, die die Werte freilegt und ein Semikolon als Trenner anfügt

      • s bedeutet ersetzen

      • ""addr:[^"]*""⇒""([^"]*)"",? der Match, der ersetzt werden soll

        • ""addr:[^"]*""⇒"" match zwei doppelte Anführungszeichen gefolgt von "add:" und einer Sequenz von beliebig vielen Nicht-Anführungszeichen ([^"]*) gefolgt von ""⇒"" gefolgt von einer Sequenz von beliebig vielen Nicht-Anführungszeichen ([^"]*) gefolgt von "" und einem optionalen Komma (,?)

      • Ersetzungsmuster ist der Inhalt der gematchten Gruppe gefolgt von einem Semikolon (\1;)

      • #g Global, also mehrfach pro Zeile

    • s#^"(.*);"#\1# - Ersetzung die die Anführungsstriche am Anfang und Ende der Zeile sowie das letzte Semikolon entfernt

      • ^ Anfang der Zeile

      • " doppelte Anführungsstriche

      • beliebig lange Folge die in einem Gruppen-Match aufgehoben wird

      • ; Semikolon

      • " doppelte Anführungsstriche

      • und da kann man noch ein $ anfügen für das Zeilenende, ist aber nicht nötig

Generell wird es aber vermutlich helfen, wenn Du Dir eine generelle Einführung in Reguläre Ausdrücke gönnst. Es gibt auch interaktive Programme, die den Prozess des Matchens zeigen - das kann auch hilfreich für das Verständnis sein. Generell baue ich solche Ausdrücke immer von links nach rechts auf. Dann kann man schrittweise testen, ob der Match noch zustande kommt.

safran

(Themenstarter)

Anmeldungsdatum:
30. April 2023

Beiträge: 5

Hi,

also die Daten sind von OpenStreetMap,

und es geht noch deutlich schlimmer:

Anbei der Export vom Feuerbacher Stadtteil Stuttgarts als CSV im Anhang.

https://media-cdn.ubuntu-de.org/forum/attachments/41/18/9375686-Feuerbach.csv Der ist richtig durcheinander.

Wüsstet Ihr, wie ich dort alle Zeilen exportiere und ordne, die PLZ, ORT, STRASSE und HAUSNUMMER beinhalten?

Mein jetziges Vorgehen ist eben mit der Zeile von rklm Danke, und den Import in Libreoffice Calc wo ich " als Trennzeichen eingebe und dann da mit C&P soweit auch weiterkomme.

Bin aber immer interessiert an Automatismen speziell aus der Shell oder Phyton.

SED finde ich sehr stark bin aber leider noch nie richtig warm für den Alltag damit geworden

Feuerbach.csv (833.4 KiB)
Download Feuerbach.csv

safran

(Themenstarter)

Anmeldungsdatum:
30. April 2023

Beiträge: 5

Hallo rklm vielen Dank für die ausführliche Erklärung. Die werde ich am Abend bei einer Tasse Tee einmal durchstudieren.

safran

(Themenstarter)

Anmeldungsdatum:
30. April 2023

Beiträge: 5

dieses mit diesen Ausdrücken in Anführungszeichen erinnert mich an ein Format, wofür es sogar eigene Befehle in der Bash gibt zum Ein- und Auslesen usw. hatte da früher sogar selber einmal was damit programmiert... Wenns mir einfällt oder ichs finde poste ichs hier.

shiro

Anmeldungsdatum:
20. Juli 2020

Beiträge: 611

Wüsstet Ihr, wie ich dort alle Zeilen exportiere und ordne, die PLZ, ORT, STRASSE und HAUSNUMMER beinhalten?

Ich denke, hier kommst du mit dem awk Ansatz am schnellsten weiter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ grep "addr.city" Feuerbach.csv | grep "addr:postcode" |
awk -F"," -v fields="addr:postcode;addr:city;addr:street;addr:housenumber" 'BEGIN{
print fields;
n=split(fields,aflds,";");
}{
gsub("\"","");
for(i=1;i<=NF;i++){split($i,a,"=>");arr[a[1]]=a[2]};
for(j=1;j<=n;j++){printf "%s",arr[aflds[j]](j==n?"\n":";")};
delete arr;
}'

Mit der Variable "fields" wird definiert, welche Spalteninhalte in welcher Reihenfolge als csv ausgegeben werden. Ich habe die awk Prozedur wegen der besseren Lesbarkeit nicht in einer Zeile geschrieben sonder verschwenderisch auf mehrere Zeilen verteilt. Mit den "grep" Befehlen werden nur die Datensätze bearbeitet, die eine Postleitzahl und einen Stadtnamen haben.

PS: Wenn du die Ausgabe sortiert nach PLZ, Stadt, Straße und Hausnummer (numerisch) sortiert haben willst, kannst du dies auch erhalten, wenn du die Ausgabe der obigen "Zeile" per Pipe in den folgenden "sort/sed" Befehl schickst:

1
$ grep ... | awk ... | sort -V | sed '$!H;1h;$!d;G'

Beim "sort -V" werden die Zahlen aufsteigend sortiert (nicht 1,10,11,2,3 sondern 1,2,3,10,11) und beim "sed" wird die durch den "sort" an das Ende beförderte "Header-Line" wieder an den Anfang gepackt (das ist aber erweiterte sed Spielerei, nichts für den Anfang).

PS2: Du kannst natürlich auch alles mit awk machen, indem du die komplette Ausgabe in die Variable o schreibst und diese an den geforkten "sort" Prozess übergibst:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
grep "addr.city" Feuerbach.csv | grep "addr:postcode" |
awk -F"," -v fields="addr:postcode;addr:city;addr:street;addr:housenumber" 'BEGIN{
n=split(fields,aflds,";");
o="";
}{
gsub("\"","");
for(i=1;i<=NF;i++){split($i,a,"=>");arr[a[1]]=a[2]};
for(j=1;j<=n;j++){o=o sprintf("%s",arr[aflds[j]](j==n?"\n":";"))};
delete arr;
}END{printf "%s", fields;print o | "sort -V"}'
Antworten |