Textliste automatisch in Felder aufteilen (SED o.ä.)

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
Ferdi
Beiträge: 22
Registriert: 27.10.2003 23:22:39
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Soest

Textliste automatisch in Felder aufteilen (SED o.ä.)

Beitrag von Ferdi » 27.10.2003 23:41:33

Ein Programm liefert als Export nur eine durch Leerzeichen getrennte
Textdatei (siehe Beispiel unten). Diese für Menschen lesbare Datei würde ich
gern durch automatisches Einfügen von Semikolon (Mehrzahl?) in eine für
Computern auswertbare Form bringen. Zu diesem Zweck habe ich bereits mit SED
experimentiert und durch den Befehl "ersetzen" die Leerzeichen vor einem
neuen Feld in ein Semikolon umwandeln können. Leider stehen zwischen den
Feldern mit fester Größe keine Leerzeichen sodaß ich mir mit diesem
Lösungsansatz die Daten des Feldes abschneide. Wie kann ich nun ein Zeichen
an beliebiger Stelle einfügen?

Ferdi

001Lieschen Mueller Musterweg 23 59555Menden
002Andreas Meier Testgasse 44 68798Dortmund
003Peter Schulz Kastanienallee 178964Regensburg

Benutzeravatar
Dookie
Beiträge: 1104
Registriert: 17.02.2002 20:38:19
Wohnort: Salzburg
Kontaktdaten:

Beitrag von Dookie » 28.10.2003 00:53:38

Hi Ferdi,

ich hab mal auf die schnelle ein Pythonscript erstellt, das Deine Datei umwandelt.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import sys

if len(sys.argv) > 1:
    if sys.argv[1] == "-h":
        print "usage: %s [infilename [outfilename]]" % (sys.argv[0],)
        sys.exit(0)
    infilename = sys.argv[1]
    infile = open(infilename,"r")
else:
    infile = sys.stdin
if len(sys.argv) > 2:
    outfilename = sys.argv[2]
    outfile = open(outfilename,"w")
else:
    outfile = sys.stdout
    
for line in infile.readlines():
    if line.strip() and line[0] != "#":
        tmp = line.strip().split()
        id = tmp[0][:3]
        vorname = tmp[0][3:]
        nachname = tmp[1]
        strasse = tmp[2]
        if len(tmp) > 4:
            strasse += " "+tmp[3]
            plz = tmp[4][:5]
            ort = tmp[4][5:]
        else:
            plz = tmp[3][:5]
            ort = tmp[3][5:]            
        outline = ";".join([id, vorname, nachname, strasse, plz, ort])
        outfile.write(outline+"\n")
testlauf:

Code: Alles auswählen

fritz@seneca:~/Python/debianforum$ more testdat.txt
001Lieschen Mueller Musterweg 23 59555Menden
002Andreas Meier Testgasse 44 68798Dortmund
003Peter Schulz Kastanienallee 1 78964Regensburg
 
fritz@seneca:~/Python/debianforum$ converter.py testdat.txt
001;Lieschen;Mueller;Musterweg 23;59555;Menden
002;Andreas;Meier;Testgasse 44;68798;Dortmund
003;Peter;Schulz;Kastanienallee 1;78964;Regensburg
fritz@seneca:~/Python/debianforum$
Ich denke, Du hast bei Deinem Beispiel ein Leerzeichen vergessen in der letzten Zeile, mein Script funktioniert auch wenn keine Hausnummer angegeben wurde, die Postleitzahl muss aber 5stellig sein!
Du kannst auch gleich eine Ausgabedatei angeben als 2. Parameter.


Gruß

Dookie

Chimerer
Beiträge: 514
Registriert: 28.01.2002 16:10:44

Beitrag von Chimerer » 28.10.2003 15:46:29

Dookie hat geschrieben:Hi Ferdi,

ich hab mal auf die schnelle ein Pythonscript erstellt, das Deine Datei umwandelt.

...
8O :D Das geht mit awk um einiges einfacher:

Code: Alles auswählen

cat datei | awk '{ printf ("%s;%s;%s;%s %s;%s;%s\n", substr($1,1,3),substr($1,4),$2,$3,$4,substr($5,1,5), substr($5,6)) }' 

als Script

Code: Alles auswählen

#!/bin/sh

cat $1 |  awk '{ printf ("%s;%s;%s;%s %s;%s;%s\n", substr($1,1,3),substr($1,4),$2,$3,$4,substr($5,1,5), substr($5,6)) }'
Als Paramter einfach die Datei angeben.

Benutzeravatar
Dookie
Beiträge: 1104
Registriert: 17.02.2002 20:38:19
Wohnort: Salzburg
Kontaktdaten:

Beitrag von Dookie » 28.10.2003 19:00:30

Hi Chimerer,

nix für ungut, aber ich hätte es auch so coden können, was aber die lesbarkeit, die flexibilität und den Komfort beeinträchtigt hätte.

Bei meinem Script kannst Du auch darauf verzichten Ein und Ausgabedatei anzugeben, dann wird einfach stdin bzw. stdout verwendet, so kann auch, ohne Änderung es Scripts die Ausgabe von einem anderen Script als Eingabe verwendet werden und die Ausgabe wieder an ein anderes Programm weigergeleitet werden.

Man könnte die Zeilen auch mit einer Regex zerlegen. Aber hauptsache es läuft.


Gruß

Dookie

Chimerer
Beiträge: 514
Registriert: 28.01.2002 16:10:44

Beitrag von Chimerer » 28.10.2003 21:12:01

Hi Dookie,
Dookie hat geschrieben:Hi Chimerer,
Bei meinem Script kannst Du auch darauf verzichten Ein und Ausgabedatei anzugeben, dann wird einfach stdin bzw. stdout verwendet, so kann auch, ohne Änderung es Scripts die Ausgabe von einem anderen Script als Eingabe verwendet werden und die Ausgabe wieder an ein anderes Programm weigergeleitet werden.
Gruß

Dookie
Funktioniert bei der awk-Variante auch ohne weiteres.
Man könnte die Zeilen auch mit einer Regex zerlegen. Aber hauptsache es läuft.
Jep. Aber meine Variante ist trotzdem doppelt so schnell. :P ;)

Code: Alles auswählen

chimerer@horizon:~$ time cat cc | ./python.py 
001;Lieschen;Mueller;Musterweg 23;59555;Menden
002;Andreas;Meier;Testgasse 44;68798;Dortmund
003;Peter;Schulz;Kastanienallee 1;78964;Regensburg

real    0m0.023s
user    0m0.020s
sys     0m0.010s

Code: Alles auswählen

chimerer@horizon:~$time cat cc | ./awk.sh    
001;Lieschen;Mueller;Musterweg 23;59555;Menden
002;Andreas;Meier;Testgasse 44;68798;Dortmund
003;Peter;Schulz;Kastanienallee 1;78964;Regensburg

real    0m0.009s
user    0m0.000s
sys     0m0.010s

Benutzeravatar
godsmacker
Beiträge: 902
Registriert: 16.03.2003 21:50:26
Lizenz eigener Beiträge: Artistic Lizenz
Wohnort: Chemnitz
Kontaktdaten:

Beitrag von godsmacker » 28.10.2003 21:40:20

Kann ich locker mit der Perl Zeile

Code: Alles auswählen

while(<>) { print join(';', ($_ =~ /^(\d{3})(\w+) (\w+) (.*?) (\d+)(\w+)$/)),"\n"; }
unterbieten:

Code: Alles auswählen

real    0m0.005s
user    0m0.000s
sys     0m0.000s
8)

Chimerer
Beiträge: 514
Registriert: 28.01.2002 16:10:44

Beitrag von Chimerer » 28.10.2003 21:45:29

godsmacker hat geschrieben:Kann ich locker mit der Perl Zeile

Code: Alles auswählen

while(<>) { print join(';', ($_ =~ /^(\d{3})(\w+) (\w+) (.*?) (\d+)(\w+)$/)),"\n"; }
unterbieten:

Code: Alles auswählen

real    0m0.005s
user    0m0.000s
sys     0m0.000s
8)
:lol: Ist jemand noch schneller?

Benutzeravatar
suntsu
Beiträge: 2947
Registriert: 03.05.2002 10:45:12
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: schweiz
Kontaktdaten:

Beitrag von suntsu » 28.10.2003 21:50:11

Also ich würde Java/StringTokenizer nehmen ;)

Wäre aba wohl ned so schnell.

edit:

Code: Alles auswählen

import java.util.*;
public class STTest {

	public static void main(String[] args) {
		String s1 = "001Lieschen Mueller Musterweg 23 59555Menden";
		 s1 = s1 +  "002Andreas Meier Testgasse 44 68798Dortmund";
		 s1 = s1 + "003Peter Schulz Kastanienallee 178964Regensburg";
		
		StringTokenizer tokenizer = new StringTokenizer( s1 );

		while ( tokenizer.hasMoreTokens() )
		     System.out.println( tokenizer.nextToken() );
	}
}
Mir ist leider noch nicht ganz klar mit was ihr da die Zeit messt ;)

gruss
manuel

Benutzeravatar
suntsu
Beiträge: 2947
Registriert: 03.05.2002 10:45:12
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: schweiz
Kontaktdaten:

Beitrag von suntsu » 28.10.2003 22:05:27

doh...

Ist ja wie in der Schule >> Ich lese die Aufgaben nie durch ;)

Code: Alles auswählen

public class STTest {
	public static void main(String[] args) {
		String s1 = "001Lieschen Mueller Musterweg 23 59555Menden";
		 s1 = s1 +  "002Andreas Meier Testgasse 44 68798Dortmund";
		 s1 = s1 + "003Peter Schulz Kastanienallee 178964Regensburg";
		s1 = s1.replace(' ',';'); 
		System.out.println(s1);
	}
}
Wäre wohl passender.

Benutzeravatar
Ferdi
Beiträge: 22
Registriert: 27.10.2003 23:22:39
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Soest

Sieht alles echt toll aus aber...

Beitrag von Ferdi » 28.10.2003 22:16:59

Vielen Dank für die vielen Lösungsmöglichkeiten. Das gibt mir mal wieder das
gute Gefühl, mit Linux das richtige System in der Hand zu haben.
Verschiedene Wege führen zu demselben Ziel - man kann sich frei entscheiden,
ob man es lieber kurz und knackig oder ordentlich strukturiert haben möchte.

AWK kenne ich schon ein wenig und habe durch Chimerers Beispiel weitere Tips
bekommen. Mit Python wollte ich mich immer schon einmal beschäftigen und
Dookies Lösungsvorschlag ist nen guter Einstieg dafür. Leider ist mein Problem
noch nicht ganz gelöst.

Zum einen ist die betreffende Datei leider bei weitem komplexer als mein
Beispiel es deutlich gemacht hat (104 Felder pro Zeile / Datensatz). Dadurch
würde bei der Python-Lösung ein enormer Programmieraufwand entstehen.

Zum anderen fehlen in einigen Datensätzen das eine oder andere Feld (wie es
Dookie bereits angedeutet hat), wodurch das AWK-Script in Schwierigkeiten
kommt da Leerzeichen ja als Trenner gelten und das leere Feld ja nicht als
Feld gewertet wird.

Ich füge deshalb nocheinmal ein Beispiel an - diesmal mit Unterstrichen als
Leerzeichen da diese mir in meinem ersten Beispiel weggekürzt wurden:

001Lieschen_______Mueller______________Auf_dem_Damm 12_________59555Menden
002Andreas________Meier________________Testgasse_44____________68798Dortmund
003Peter__________Schulz_______________________________________78964Regensburg
004Sidam_Al_______Sechmet______________Kastanienallee_23____________Berlin

Ein Mensch sieht die Struktur der Liste (bei nicht proportionaler Schrift)auf
den ersten Blick und könnte durch daß "stumpfe" Einfügen von z.B. einem
Tabulator am Beginn eines jeden Feldes die Liste in Felder aufteilen. Das
anschließende Herausfiltern der überschüssigen Leerzeichen pro Feld ist dann nur
noch ein Klacks und würde die Anzahl der Felder pro Datensatz nicht verändern.

Benutzeravatar
suntsu
Beiträge: 2947
Registriert: 03.05.2002 10:45:12
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: schweiz
Kontaktdaten:

Beitrag von suntsu » 28.10.2003 22:20:46

Immo muss man das ziemlich hart ausprogrammieren, und ist warscheinlich trotzdem nicht vor Fehlern sicher.
Wer auf der Welt exportiert ein solches Format?

gruss
manuel

edit: oder ist das Tab-Delemited?
Zuletzt geändert von suntsu am 28.10.2003 22:33:51, insgesamt 1-mal geändert.

Benutzeravatar
Dookie
Beiträge: 1104
Registriert: 17.02.2002 20:38:19
Wohnort: Salzburg
Kontaktdaten:

Beitrag von Dookie » 28.10.2003 22:32:53

Hi Ferdi,

setze Deine Beispiele innerhalb von [code]..[/code], dann werden auch keine Leerzeichen weggeküttzt.


Gruß

Dookie]

Benutzeravatar
godsmacker
Beiträge: 902
Registriert: 16.03.2003 21:50:26
Lizenz eigener Beiträge: Artistic Lizenz
Wohnort: Chemnitz
Kontaktdaten:

Beitrag von godsmacker » 28.10.2003 23:12:39

suntsu hat geschrieben:Mir ist leider noch nicht ganz klar mit was ihr da die Zeit messt ;)
Mit ``time'' 8)

Gruß,
Florian

Benutzeravatar
Ferdi
Beiträge: 22
Registriert: 27.10.2003 23:22:39
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Soest

Trennung klappt mit ein wenig AWKerei

Beitrag von Ferdi » 01.11.2003 12:03:45

Vielen Dank an alle. Habe mir aus den geposteten Antworten ein (etwas kompliziertes) Script zurechtgebastelt, welches meinen Anforderungen genügt.

Ferdi

Antworten