Forum: PC-Programmierung maximale Länge csv Spalten


von Gargamel (Gast)


Lesenswert?

Hi
ich hab ein csv mit etwa 20 Spalten und 10000 Zeilen. WIe kann ich die 
max. Länge jeder Spalte ermitteln (in Linux)?

das Beispiel:
Hund;Katze;Maus
Peter;Udo,Robert

sollte dann zu folgendem Ergebnis führen
5;5;6

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Python- oder Perlscript.

Perl ist hier nicht so beliebt :-), aber da geht das mit einem 
Einzeiler:
1
$ perl -e 'while(<>){chomp;@x=split(";");$i=0;foreach $v(@x){$l=length($v);$s[$i]=$l unless $s[$i]>$l;$i++};} END {print join(";", @s) . "\n"}' test.csv
2
5;5;6
Ich habe mal das Komma in der zweiten Zeile deiner Testdaten in ein 
Semikolon geändert.

Falls Interesse besteht, kann ich den Einzeiler natürlich auch nochmal 
aufhübschen und kommentieren. Ansonsten schreib's dir in Python.

Geht sicher auch mit Shell-Bordmitteln, wird aber weder schneller noch 
schöner davon.

von Karl Käfer (Gast)


Lesenswert?

Gargamel schrieb:
> ich hab ein csv mit etwa 20 Spalten und 10000 Zeilen. WIe kann ich die
> max. Länge jeder Spalte ermitteln (in Linux)?
>
> das Beispiel:
> Hund;Katze;Maus
> Peter;Udo,Robert
>
> sollte dann zu folgendem Ergebnis führen
> 5;5;6

Es ist nur ein Schnellschuß, scheint aber zu funktionieren, wenn ich 
Deine Beispieldaten korrigiere (zweiter Delimiter der zweiten Zeile):
1
#!/usr/bin/env python
2
import sys
3
import csv
4
from argparse import ArgumentParser
5
6
7
def main():
8
    parser = ArgumentParser(description='calculate column lengths')
9
    parser.add_argument('filename', help='CSV file')
10
    parser.add_argument('--delimiter', '-d', type=str, default=';',
11
                        help='the delimiter')
12
    args = parser.parse_args()
13
14
    with open(args.filename, 'r') as ifh:
15
        reader = csv.reader(ifh, delimiter=args.delimiter)
16
        data = [[len(point) for point in row] for row in reader]
17
    #
18
    data = list(map(max, zip(*data)))
19
    writer = csv.writer(sys.stdout, delimiter=args.delimiter)
20
    writer.writerow(data)
21
22
23
if __name__ == '__main__':
24
    main()

Eine Alternative wäre das Programm "csvstat" aus dem ebenfalls in Python 
geschriebenen Paket "csvkit". Das lohnt sich sehr, wenn man häufiger mit 
CSV-Dateien zu tun hat.

von Karl Käfer (Gast)


Lesenswert?

Jörg W. schrieb:
> Perl ist hier nicht so beliebt :-), aber da geht das mit einem
> Einzeiler:
> $ perl -e 'while(<>){chomp;@x=split(";");$i=0;foreach
> $v(@x){$l=length($v);$s[$i]=$l unless $s[$i]>$l;$i++};} END {print
> join(";", @s) . "\n"}' test.csv
> 5;5;6
1
csv.writer(sys.stdout, delimiter=';').writerow(list(map(max, zip(*[[len(p) for p in r] for r in csv.reader(open(sys.argv[1], 'r'), delimiter=';')]))))

> Falls Interesse besteht, kann ich den Einzeiler natürlich auch nochmal
> aufhübschen und kommentieren. Ansonsten schreib's dir in Python.

Als Oneliner in Python ist es weder kürzer noch lesbarer, fürchte ich. 
Aber Python hat einen kleinen Vorteil: wenn in einem Datenpunkt der 
Delimiter enthalten und er darum gequotet ist, fällt es nicht auf die 
Nase. Ich bin immer noch sehr häufig mit CSVs unterwegs und weiß daher: 
das manuell zu parsen oder zu schreiben endet immer böse. Für Perl gibt 
es daher im CPAN das Modul Text::CSV, das aber erst installiert werden 
muß. :)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oneliner in Python sind aufgrund der Zwangs-Einrückungen sowieso stark 
limitiert. Daran versuche ich mich gar nicht erst. In Perl nehme ich sie 
gern für "Eintags-Probleme" (Sachen, die ich nur jetzt und hier brauche 
und dann erst im nächsten Jahrhundert wieder).

Klar, wenn man CSVs hat, die nicht mehr so simpel sind, ist es immer 
besser, einen der vorgefertigten Module zu benutzen.

von Gargamel (Gast)


Lesenswert?

Jörg W. schrieb:
> Perl ist hier nicht so beliebt :-), aber da geht das mit einem
> Einzeiler:
> $ perl -e 'while(<>){chomp;@x=split(";");$i=0;foreach
> $v(@x){$l=length($v);$s[$i]=$l unless $s[$i]>$l;$i++};} END {print
> join(";", @s) . "\n"}' test.csv
> 5;5;6

vielen Dank, hat auf Anhieb geklappt.
Nur hab ich gemerkt, dass manche Zeilen vermutlich mehr Spalten haben 
(also mehr ";") als eigentlich geplant.
Wie kann ich das csv auf zwei oder mehrere Dateien aufteilen, sodass in 
ok.csv die Zeilen geschrieben werden, die genau 20 SPalten haben und in 
einer oder mehreren anderen Dateien die restlichen Zeilen?

von Gargamel (Gast)


Lesenswert?

Gargamel schrieb:
> Jörg W. schrieb:
>> Perl ist hier nicht so beliebt :-), aber da geht das mit einem
>> Einzeiler:
>> $ perl -e 'while(<>){chomp;@x=split(";");$i=0;foreach
>> $v(@x){$l=length($v);$s[$i]=$l unless $s[$i]>$l;$i++};} END {print
>> join(";", @s) . "\n"}' test.csv
>> 5;5;6
>
> vielen Dank, hat auf Anhieb geklappt.
> Nur hab ich gemerkt, dass manche Zeilen vermutlich mehr Spalten haben
> (also mehr ";") als eigentlich geplant.
> Wie kann ich das csv auf zwei oder mehrere Dateien aufteilen, sodass in
> ok.csv die Zeilen geschrieben werden, die genau 20 SPalten haben und in
> einer oder mehreren anderen Dateien die restlichen Zeilen?

ich hab grad bemekrt an was es liegt.
Einige Zeilen haben ein Semicolon in einem Eintrag, dieser wird mit 
quotation marks abgekrenzt. z.B:

Hund;Katze;Maus
Peter;Udo;Robert
0815;4711;"24;7"

wie kann ich das perl script insofern abändern, dass bei einem semicolon 
innerhalb quotation marks nicht eine neue Spalte gezählt wird?

von Karl Käfer (Gast)


Lesenswert?

Gargamel schrieb:
> ich hab grad bemekrt an was es liegt.
> Einige Zeilen haben ein Semicolon in einem Eintrag, dieser wird mit
> quotation marks abgekrenzt. z.B:
>
> Hund;Katze;Maus
> Peter;Udo;Robert
> 0815;4711;"24;7"
>
> wie kann ich das perl script insofern abändern, dass bei einem semicolon
> innerhalb quotation marks nicht eine neue Spalte gezählt wird?

Indem Du mein Python-Skript benutzt, das kann damit umgehen.

Oder indem Du Jörgs Perlskript so änderst, daß es das Perl-Modul 
Text::CSV benutzt, das Du allerdings aus dem CPAN installieren müßtest.

von Gargamel (Gast)


Lesenswert?

Karl Käfer schrieb:
> Indem Du mein Python-Skript benutzt, das kann damit umgehen.

hab ich grad gemacht, hat einwandfrei geklappt, besten Dank!

von Karl Käfer (Gast)


Lesenswert?

Gargamel schrieb:
> Karl Käfer schrieb:
>> Indem Du mein Python-Skript benutzt, das kann damit umgehen.
>
> hab ich grad gemacht, hat einwandfrei geklappt, besten Dank!

Immer gerne. Freut mich sehr, daß es geklappt hat, lieben Dank für die 
Rückmeldung! ;)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl Käfer schrieb:
> Indem Du mein Python-Skript benutzt, das kann damit umgehen.

Das wäre jetzt für den Fall auch meine Empfehlung gewesen. ;-)

So ein Einzeiler ist als "quick&dirty" praktikabel für genau den Zweck, 
für den er gezimmert wurden ist. Wenn da mehr Sonderfälle drin sind, 
dann macht man es besser richtig (und das wäre hier halt ein 
"offizieller" Modul, der CSV-Dateien handhabt).

Persönlich mag ich CSV-Dateien allerdings überhaupt nicht, bei denen das 
Trennzeichen auch noch im Eintrag auftreten können soll. Für mich ist an 
der Stelle dann die Grenze der Nützlichkeit von CSV erreicht.

von Karl Käfer (Gast)


Lesenswert?

Jörg W. schrieb:
> Das wäre jetzt für den Fall auch meine Empfehlung gewesen. ;-)

Das ehrt und freut mich, lieben Dank!

> So ein Einzeiler ist als "quick&dirty" praktikabel für genau den Zweck,
> für den er gezimmert wurden ist. Wenn da mehr Sonderfälle drin sind,
> dann macht man es besser richtig (und das wäre hier halt ein
> "offizieller" Modul, der CSV-Dateien handhabt).

Das wäre mit Perl und Text::CSV ja genauso, Python hat sein csv-Modul 
halt schon an Bord und beides ist auf so ziemlich jedem Linux 
installiert. Wenn ich mich Recht entsinne, wurde Python sogar mal im 
Filesystem Hierarchy Standard oder der Linux Standard Base erwähnt.

> Persönlich mag ich CSV-Dateien allerdings überhaupt nicht, bei denen das
> Trennzeichen auch noch im Eintrag auftreten können soll. Für mich ist an
> der Stelle dann die Grenze der Nützlichkeit von CSV erreicht.

Hast schon Recht. CSVs sind außerdem ziemlich teuer, weil beim Lesen 
immer Zeichen für Zeichen verglichen werden muß, und leider kann es auch 
mehrere Dimensionen nicht gut abbilden. Andererseits sind CSVs ein 
universelles Format zum Datenaustausch, kann zur Not auch von Hand mit 
etwas wie more(1) oder less(1) (oder csvlook aus dem Paket csvkit) 
gelesen oder im Fall von Fehlern repariert werden und ist obendrein gut 
komprimierbar. Da habe ich schon sehr viel Schlimmeres erlebt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.