CSV Separierung mit Tücken

Hallo,

ich möchte eine CSV-Datei in perl verwalten, die über Kommata separiert ist.
Das Dumme an der Sache ist, dass eventuell in einem über Anführungszeichen separierten Teilstring wieder Kommata enthalten sein dürfen.

Die Datei sieht also etwa so aus:

"ABC",12,"D,E",,, 

wobei „D,E“ als ein Eintrag zählen würde.

Meine laienhaften Versuche führten mich bisher so weit:

my $w="(\".+?\")";
foreach $line ()
 {
 if ( $line =~ m/^Event\:blush:w\,$w\,$w\,$w\,$w\,$w\,$w\,$w\,$w/ )
 {
 # do line-by-line processing.
 }
 }

Das ist aber weder des Rätsels Lösung, noch korrekt und außerdem unübersichtlich.

Gibt es eine elegante Möglichkeit, derlei Dinge auszuwerten, ohne zeichenweise Stringzerlegung zu betreiben?

Dankeschön und Gruss,
Mike

Hallo,

ich möchte eine CSV-Datei in perl verwalten, die über Kommata
separiert ist.
Das Dumme an der Sache ist, dass eventuell in einem über
Anführungszeichen separierten Teilstring wieder Kommata
enthalten sein dürfen.

Die Datei sieht also etwa so aus:

„ABC“,12,„D,E“,

wobei „D,E“ als ein Eintrag zählen würde.

Das einfachste wäre vermutlich folgendes:
Den String entlang der Kommata splitten, dann die Stücke durchgehen, und wenn eines mit " anfängt, die nächsten daran kleben, bis wieder ein " kommt.

Grüße,
Moritz

Hallo Mike

ich möchte eine CSV-Datei in perl verwalten, die über Kommata
separiert ist.
Das Dumme an der Sache ist, dass eventuell in einem über
Anführungszeichen separierten Teilstring wieder Kommata
enthalten sein dürfen.

Die Datei sieht also etwa so aus:

„ABC“,12,„D,E“,

wobei „D,E“ als ein Eintrag zählen würde.

Meine laienhaften Versuche führten mich bisher so weit:

if ( $line =~m/^Event:blush:w,$w,$w,$w,$w,$w,$w,$w,$w/ )

do line-by-line processing.

}

Gibt es eine elegante Möglichkeit, derlei Dinge auszuwerten,
ohne zeichenweise Stringzerlegung zu betreiben?

Es gibt mehrere Möglichkeiten,

  1. über Text::CSV, etwa so

    #!/usr/bin/perl
    use Text::CSV;

    my $csvfile = ‚daten.csv‘;
    open( my $fh, 'new();
    while( ) {
    if( $csv->parse($_) ){
    my @daten = $csv->fields();
    print „@daten\n“;
    }
    }
    close $fh;

oder
2) über regex:

 #!/usr/bin/perl

 my $csvfile = 'daten.csv';
 open( my $fh, ' ) {
 my @daten = grep defined, /(?:^|,) (?: "([^"]+)" | ([^,]+))/xg;
 print "@daten\n";

 } 
 close $fh;

Grüße

CMБ

Hallo, Semjon.

Es gibt mehrere Möglichkeiten,

  1. über Text::CSV, etwa so

#!/usr/bin/perl
use Text::CSV;

Das klingt gut, jedoch habe ich bisher noch keine vernünftige Text::CSV Library gefunden. Ich habe eine Solaris (Sun5) Umgebung, also kann ich mit den schnell findbaren Linux/Windows Builds nichts anfangen.
Idealerweise bräuchte ich sogar einen maschinenunabhängigen Build, da ich nicht weiß, wo ich betreffende Komponenten zukünftig ausführen möchte.

  1. über regex:

#!/usr/bin/perl

Das scheint dann also erstmal der gangbare Weg zu sein, danke, ist viel kürzer als das, was ich mir mittlerweile zusammengewurschtelt habe.

Danke,
Mike

Hallo,

Es gibt mehrere Möglichkeiten,

  1. über Text::CSV, etwa so

#!/usr/bin/perl
use Text::CSV;

Das klingt gut, jedoch habe ich bisher noch keine vernünftige
Text::CSV Library gefunden.

Es geht nicht um ein , sondern um das Text::CSV Modul, und zwar von CPAN: http://search.cpan.org/~alancitt/Text-CSV-0.01/CSV.pm

Das solltest du mit

perl -MCPAN -e 'install Text::CSV'

Voll automatisiert installieren können.

Ich habe eine Solaris (Sun5)
Umgebung, also kann ich mit den schnell findbaren
Linux/Windows Builds nichts anfangen.
Idealerweise bräuchte ich sogar einen maschinenunabhängigen
Build, da ich nicht weiß, wo ich betreffende Komponenten
zukünftig ausführen möchte.

Die CPAN-Sachen sollten eigentlich portabel sein.

Grüße,
Moritz

Hallo, Moritz.

Es geht nicht um ein , sondern um das Text::CSV Modul, und zwar von CPAN:
http://search.cpan.org/~alancitt/Text-CSV-0.01/CSV.pm

Da hatte ich dann was falsch verstanden, naja Google ist nicht immer der direkte Weg. Danke jedenfalls.

Jetzt muß ich mir nur noch das Okay holen, solche Libs verwenden zu dürfen dann bin ich direkt ein ganzes Stück weiter. Die sind doch „public license“, oder?

Gruss,
Mik

  1. über regex:

#!/usr/bin/perl

my $csvfile = ‚daten.csv‘;
open( my $fh, ’ ) {
my @daten = grep defined, /(?:^|,) (?: „([^“]+)" |
([^,]+))/xg;
print „@daten\n“;

}
close $fh;

Grüße

schon nicht schlecht. aber wenn csv so einfach waere… :smile:

1.normale zelle,2.normale zelle
"1.zelle mit um-
bruch","2.zelle mit um-
bruch"
"1. Zelle mit ""Quotes""","2.Zelle mit ""Quotes"""
"""1. Zelle in ""Quotes""""","""2. Zelle in ""Quotes"""""
"1. Zelle mit , Trennzeichen","2. Zelle mit , Trennzeichen"
"1. Zelle mit "","" Trennzeichen in Quotes","2. Zelle mit "","" Trennzeichen in Quotes"
"1. Zelle mit , Trennzeichen und ""Quotes""","2. Zelle mit , Trennzeichen und ""Quotes"""
"""1. Zelle in Quotes um-
bruch und , Trennzeichen und ""Quotes""""","""2. Zelle in Quotes um-
bruch und , Trennzeichen und ""Quotes"""""

Hallo dog.je,

schon nicht schlecht. aber wenn csv so einfach waere… :smile:

1.normale zelle,2.normale zelle
„1.zelle mit um-
bruch“,„2.zelle mit um-
bruch“

OK, beim ersten draufscheuen musste ich erstmal
schlucken, aber Deine Beispiele sind noch so
einfach, dass ich meinen regulären Ausdruck
nahezu unverändert verwenden konnte. Das einzige,
was noch zu machen ist, ist eine gewisse
Vorformatierung der Records ( „\n“ -> „~“ und „“"" -> „“),
siehe:
$/ = ‚‘; # paragraph mode \n\n
my $rg = qr/(?: \A | , | (? ) {
chomp; y!\n!~!d; s!""!!g;

my @daten = grep defined, /$rg/g;

print scalar @daten, (map " [$_]", @daten), „\n“
}

__DATA__
„1.zelle mit um-
bruch“,„2.zelle mit um-
bruch“

„1. Zelle mit „„Quotes“““,„2.Zelle mit „„Quotes“““

„“„1. Zelle in „„Quotes“““"",""„2. Zelle in „„Quotes“““""

„1. Zelle mit , Trennzeichen“,„2. Zelle mit , Trennzeichen“

„1. Zelle mit „“,“" Trennzeichen in Quotes",„2. Zelle mit „“,“" Trennzeichen in Quotes"

„1. Zelle mit , Trennzeichen und „„Quotes“““,„2. Zelle mit , Trennzeichen und „„Quotes“““

„“„1. Zelle in Quotes um-
bruch und , Trennzeichen und „„Quotes“““"",""„2. Zelle in Quotes um-
bruch und , Trennzeichen und „„Quotes“““""

1.normale zelle,2.normale zelle

Aber Du denkst Dir bestimmt noch etwas
Komplizierteres aus :wink:

Grüße

CMБ

Hallo,

Es geht nicht um ein , sondern um das Text::CSV Modul, und zwar von CPAN:
http://search.cpan.org/~alancitt/Text-CSV-0.01/CSV.pm

Da hatte ich dann was falsch verstanden, naja Google ist nicht
immer der direkte Weg. Danke jedenfalls.

Bitteschoen :wink:

Jetzt muß ich mir nur noch das Okay holen, solche Libs
verwenden zu dürfen dann bin ich direkt ein ganzes Stück
weiter. Die sind doch „public license“, oder?

Die meisten CPAN-Module sind unter der gleichen Lizenz veroeffentlicht wie perl selbst.

Bei diesem Modul steht es in der Dokumentation nicht drin, aber wenn man dem „Source“-Link folgt, steht da

# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.

(von http://search.cpan.org/src/ALANCITT/Text-CSV-0.01/CS…)

Das sollte fuer fast alle Zwecke frei genug sein :wink:

Gruesse,
Moritz

ot Re^4: CSV Separierung mit Tücken

OK, beim ersten draufscheuen musste ich erstmal
schlucken, aber Deine Beispiele sind noch so
einfach, dass ich meinen regulären Ausdruck
nahezu unverändert verwenden konnte.

hier ist er z.b. gestolpert.
[1. Zelle mit] [Trennzeichen"] [2. Zelle mit , Trennzeichen]

sagen wir mal so: mein php-klasse hat 100 zeilen mehr und duerfte etwas mehr speicher brauchen - output siehe unten. na gut, immerhin kann sie mir das ergebnis bei csv mit kopfzeile auch als named array zurueckgeben - das ist ab 20 spalten hilfreich.

ich finde es immer erstaunlich, was ihr aus der zeichenmenge [@%~ynqr?:] - was wahrscheinlich schon wieder ein „sinnvoller“ perlausdruck ist - macht.

Aber Du denkst Dir bestimmt noch etwas
Komplizierteres aus :wink:

von wg. der pruefbarkeit muesste man jeweils noch 3. dritte spalte setzen.

aber eigentlich waren das alles faelle aus der praxis, die mir schon den 1 oder anderen import zerschossen haben. ok, quotes an trennzeichen sind mir beim posten eingefallen, sind aber wiederum auch nicht so weit hergeholt.

mein klasse rafft das mit den gequoteten trennzeichen noch nicht:

Array
(
 [0] =\> Array
 (
 [0] =\> 
 )

 [1] =\> Array
 (
 [0] =\> 1.normale zelle
 [1] =\> 2.normale zelle
 )

 [2] =\> Array
 (
 [0] =\> 1.zelle mit um-
bruch
 [1] =\> 2.zelle mit um-
bruch
 )

 [3] =\> Array
 (
 [0] =\> 1. Zelle mit "Quotes"
 [1] =\> 2. Zelle mit "Quotes"
 )

 [4] =\> Array
 (
 [0] =\> "1. Zelle in "Quotes""
 [1] =\> "2. Zelle in "Quotes""
 )

 [5] =\> Array
 (
 [0] =\> 1. Zelle mit , Trennzeichen
 [1] =\> 2. Zelle mit , Trennzeichen
 )

 [6] =\> Array
 (
 [0] =\> 1. Zelle mit "
 [1] =\> " Trennzeichen in Quotes
 [2] =\> 2. Zelle mit "
 [3] =\> " Trennzeichen in Quotes
 )

 [7] =\> Array
 (
 [0] =\> 1. Zelle mit "Quotes vor"
 [1] =\> Trennzeichen"
 [2] =\> 2. Zelle mit "Quotes vor"
 [3] =\> Trennzeichen"
 )

 [8] =\> Array
 (
 [0] =\> 1. Zelle mit , Trennzeichen und "Quotes"
 [1] =\> 2. Zelle mit , Trennzeichen und "Quotes"
 )

 [9] =\> Array
 (
 [0] =\> "1. Zelle in Quotes um-
bruch und , Trennzeichen und "Quotes""
 [1] =\> "2. Zelle in Quotes um-
bruch und , Trennzeichen und "Quotes""
 )

)

Hallo dog.je

hier ist er z.b. gestolpert.
[1. Zelle mit] [Trennzeichen"] [2. Zelle mit ,
Trennzeichen]

Du hast recht, ich habe Mist gebaut. Muss ich
noch mal ran :wink:

sagen wir mal so: mein php-klasse hat 100 zeilen mehr und
duerfte etwas mehr speicher brauchen - output siehe unten. na
gut, immerhin kann sie mir das ergebnis bei csv mit kopfzeile
auch als named array zurueckgeben - das ist ab 20 spalten
hilfreich.

100 Zeilen? Mit Kommentaren und Handbuch, oder :wink:

ich finde es immer erstaunlich, was ihr aus der zeichenmenge
[@%~ynqr?:] - was wahrscheinlich schon wieder ein „sinnvoller“
perlausdruck ist - macht.

Warte, was nun kommt … :wink:

von wg. der pruefbarkeit muesste man jeweils noch 3. dritte
spalte setzen.

OK

mein klasse rafft das mit den gequoteten trennzeichen noch
nicht:

Array

Jetzt klappts auch mit N Feldern pro Zeile
(leider ist der reguläre Ausdruck 2 Zeilen
länger geworden …)
my ($rg, $r);

$rg = qr/(?:frowning:[^",]+?)(?:,|\z))|(?:frowning:?{$r=0})
(?:"(?{$r++})|\A)((?:[^"]+|(?:"")+
)+)(?(?{$r&1})"(?!")(?{$r++})|(?:,|
\s*))(?:,|\z ))/xs;

$/ = ‚‘; # set paragraph mode \n\n
while( ) {
chomp; y/\n/~/d; # Zeilenumbruch => ~
my @daten = grep defined, /$rg/g; # extrahieren
print scalar @daten, " felder: ", (map "($_) ", @daten), „\n“
}

__DATA__
„1.zelle mit um-
bruch“,„2.zelle mit um-
bruch“

„1. Zelle mit „„Quotes“““,„2.Zelle mit „„Quotes“““

„“„1. Zelle in „„Quotes“““"",""„2. Zelle in „„Quotes“““""

„1. Zelle mit , Trennzeichen“,„2. Zelle mit , Trennzeichen“

„1. Zelle mit „“,“" Trennzeichen in Quotes",„2. Zelle mit „“,“" Trennzeichen
in Quotes",„3. Zelle mit „“,“" Trennzeichen in Quotes nach Umbruch"

„1. Zelle mit , Trennzeichen und „„Quotes“““,„2. Zelle mit , Trennzeichen und „„Quotes“““

„“„1. Zelle in Quotes um-
bruch und , Trennzeichen und „„Quotes“““"",""„2. Zelle in Quotes um-
bruch und , Trennzeichen und „„Quotes“““""

1.normale zelle,2.normale zelle

Grüße

CMБ

Hallo Semjon & others,

ihr brucht das Rad nicht immer neu zu erfinden. Bei Friedl (Mastering Regular Expressions, Jeffrey E. F. Friedl, O’Reilly, siehe http://www.oreilly.com/catalog/regex3) gibt es ein ganzes Kapitel, das sich nur mit Perl und CSV beschäftigt.

gruss
bernhard

Hallo Bernhard

ihr brucht das Rad nicht immer neu zu erfinden. Bei Friedl
(Mastering Regular Expressions, Jeffrey E. F. Friedl,
O’Reilly, siehe http://www.oreilly.com/catalog/regex3) gibt es
ein ganzes Kapitel, das sich nur mit Perl und CSV beschäftigt.

Oha! Tatsache. Gegenüber meiner 2’ed (2002) hat
sich ja Einiges in der 3’ed verändert. Werd ich
mir wohl bestellen müssen …

Aber ungeachtet dessen ist eine „hinreichend
komplizierte“ Regexübung eine gute Vorbeugung
gegen Alters-Demenz :wink:

Grüße & Danke für den Tipp

CMБ

Hallo Semjon & others,

ihr brucht das Rad nicht immer neu zu erfinden.

Nein, das muß man wirklich nicht

O’Reilly, siehe http://www.oreilly.com/catalog/regex3) gibt es
ein ganzes Kapitel, das sich nur mit Perl und CSV beschäftigt.

Mal schaun, wann es geliefert wird, das könnte sich auch anderweitig nützlich erweisen :wink:

Dankeschön.

Gruss,
Mike