maximale Länge csv Spalten

Moderator Persönliche Seite #7302703
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.

Gast #7302706
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.

Gast #7302717
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ß. :)

Moderator Persönliche Seite #7302729
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.

Gast #7302776
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?

Gast #7302783
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?

Gast #7302787
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.

Moderator Persönliche Seite #7302849
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.

Gast #7303892
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren