2016-04-12 7 views
-1

Ich habe ein Problem mit einem Programm, das durch eine CSV-Datei mit ein paar Millionen Datensätze durchsucht: zwei Felder in jeder Zeile hat Kommentare, die Benutzer eingegeben haben und manchmal verwenden sie Kommas in ihren Kommentaren. Wenn Kommas eingegeben werden, wird dieses Feld in doppelte Anführungszeichen gesetzt. Ich muss alle Kommas in diesen Feldern durch ein Leerzeichen ersetzen. Hier ist eine solche Zeile aus der Datei Ihnen eine Idee zu geben -Komma durch Leerzeichen in nur einem Feld ersetzen - aus einer CSV-Datei

1925,47365,2,650187016,1,1,"MADE FOR DRAWDOWNS, NEVER P/U",16,IFC 8112NP,Standalone-6,,,44,10/22/2015,91607,,B24W02651,,"PA-3, PURE",4/28/2015,1,0,,1,MAN,,CUST,,CUSTOM MATCH,0,TRUE,TRUE,O,C48A0D001EF449E3AB97F0B98C811B1B,POS.MISTINT.V0000.UP.Q,PROD_SMISA_BK,414D512050524F445F504F5331393235906F28561D2F0020,10/22/2015 9:29,10/22/2015 9:30 

HINWEIS - Ich habe nicht den Text hat :: CSV-Modul zur Verfügung zu mir, noch wird es in dem Server verwende ich zur Verfügung gestellt werden.

Hier ist ein Teil meines Codes in der Analyse dieser Datei. Das erste, was ich mache, ist die Verkettung der allerersten drei Felder und das Voranstellen dieses verketteten Feldes an jede Zeile. Dann möchte ich die Kommata in @fields [7,19] löschen, dann das DATUM in drei Feldern und das DATETIME in zwei Feldern formatieren. Die einzige Linie, die ich kann nicht herausfinden, ist diese Kommata Entrümpelung -

my @data; 

# Read the lines one by one. 

while ($line = <$FH>) { 

    # split the fields, concatenate the first three fields, 
    # and add it to the beginning of each line in the file 

    chomp($line); 
    my @fields = split(/,/, $line); 

    unshift @fields, join '_', @fields[0..2]; 

    # remove user input commas in fields[7,19] 

    $_ =     for fields[7,19]; 

    # format DATE and DATETIME fields for MySQL/sqlbatch60 

    $_ = join '-', (split /\//)[2,0,1] for @fields[14,20,23]; 
    $_ = Time::Piece->strptime($_,'%m/%d/%Y %H:%M')->strftime('%Y-%m-%d %H:%M') for @fields[38,39]; 

    # write the parsed record back to the file 

    push @data, \@fields; 
} 
+3

Wie parst du eine CSV-Datei korrekt? Sie verwenden ein gut gestaltetes, gut getestetes Modul wie [Text :: CSV] (https://metacpan.org/pod/Text::CSV). –

+1

Die "_up to_" 30 ist ein Problem - was ist, wenn es 10 Zeichen ist, mit ein paar Kommas und dann ein legitimes Komma (Feldtrennzeichen)? Gibt es etwas, das Sie nach Feld 7 mit Sicherheit über bestimmte Felder wissen? (Zum Beispiel, "ah, Feld 10 muss immer mit ... beginnen") – zdim

+0

@zdim - Jetzt wird mir gesagt, dass es in diesem Feld doppelte Anführungszeichen geben wird, wenn ein Komma vom Benutzer eingegeben wurde. Kann ** tr /,//; ** verwendet werden? – BigRedEO

Antwort

0

Text::CSV_XS Griffe zitierte Komma gerade fein:

#!/usr/bin/perl 
use warnings; 
use strict; 

use Text::CSV_XS qw{ csv }; 

my $aoa = csv(in => 'file.csv'); # The file contains the sample line. 
print $aoa->[0][6]; 
1

Wenn es NUR das achte Feld, das ist beunruhigend UND Sie wissen genau, wie viele Felder es sein sollte, können Sie es auf diese Weise tun

Angenommen, die Gesamtanzahl der Felder ist immer N

  • Split die Linie auf Kommata ,
  • Separate und speichern Sie die ersten sechs Felder
  • Separate und speichern Sie die letzten n Felder, wobei n N-8
  • Rejoin was mit Komma bleibt , . Dies bildet jetzt Feld 8

und dann tun, was auch immer Sie damit tun möchten. Zum Beispiel, schreiben Sie es auf eine richtige CSV-Datei

+0

Ich habe Ihre Lösung bearbeitet, da ich denke, dass Ihre Idee ausgezeichnet ist und erklärt werden musste ein bisschen besser. Bitte setze meine Bearbeitung zurück, wenn du meinen Änderungen nicht zustimmst. – Borodin

0

Hinweis   Die beiden Hauptversionen unter einem Feld aufzuräumen. Die letzte Änderung in der Frage besagt, dass es in der Tat zwei solche Felder gibt. Der Code kann dafür modifiziert werden, aber ich werde zuerst auf ein Feedback dazu warten. Die dritte Version, am Ende, arbeitet mit einer beliebigen Anzahl von fehlerhaften Feldern. Der gesamte Code wurde mit dem mitgelieferten Beispiel und seinen Variationen getestet.


Folgende Klarstellungen betreffen den Fall, wenn die Datei von Hand bearbeitet werden muss. Ein Modul wird zum Analysieren von .csv leicht empfohlen, aber es gibt ein Problem hier: Vertrauen auf den Benutzer, doppelte Anführungszeichen einzugeben. Wenn sie nicht da sind, haben wir eine fehlerhafte Datei.

Ich nehme an, dass die Anzahl der Felder in der Datei mit Sicherheit und vor der Zeit bekannt ist.

Die beiden folgenden unabhängigen Lösungen verwenden entweder Array- oder String-Verarbeitung.

(1) Die Datei wird sowieso Zeile für Zeile verarbeitet, die Zeile ist bereits aufgeteilt. Wenn mehr Felder als erwartet vorhanden sind, verbinden Sie die zusätzlichen Array-Elemente nach dem Space und überschreiben Sie dann das Array mit den richtigen Feldern. Dies ist ähnlich wie in der Antwort von vanHoesel beschrieben.

(2) Führen Sie eine Vorverarbeitung der Datei durch, indem Sie nur nach fehlerhaften Zeilen suchen und diese nach Bedarf korrigieren. Verwendet String-Manipulationen. (Oder die obige Methode kann verwendet werden.) Die $num_fields und $ibad sind identisch.

while (my $line = <$fh>) { 
    # Number of fields: commas + 1 (tr|,|| counts number of ",") 
    my $have_fields = $line =~ tr|,|| + 1; 
    if ($have_fields != $num_fields) { 
     # Get indices of commas delimiting the bad field 
     my ($beg, $end) = map { 
      my $p = '[^,]*,' x $_; 
      $line =~ /^$p/ and $+[0]-1; 
     } ($ibad, $ibad+$have_fields-$num_fields); 

     # Replace extra commas and overwrite that part of the string 
     my $bad_field = substr($line, $beg+1, $end-$beg-1); 
     (my $fixed = $bad_field) =~ tr/,/ /; 
     substr($line, $beg+1, $end-$beg-1) = $fixed; 
    } 
    # Perhaps write the line out, for a corrected .csv file 
    print $line; 
} 

In der letzten Zeile wird durch die Zuordnung zu substr der schlechten Teil der $line überschrieben, was ermöglicht diese Funktion. Der neue Teilstring $fixed wird mit Kommas versehen, die geändert (oder entfernt, falls gewünscht) und zum Überschreiben des schlechten Teils desverwendet werden. Siehe Dokumentation.


Wenn Zitate bekannt sind, kann ein Regex verwendet werden. Dies funktioniert mit einer beliebigen Anzahl von fehlerhaften Feldern.

while (my $line = <$fh>) { 
    $line =~ s/."([^"]+)"/join ' ', split(',', $1)/eg; # " 
    # process the line. note that double quotes are removed 
} 

Wenn die Anführungszeichen sind verschieben Sie sie in Klammern gehalten werden, als auch erfasst werden.

Diese Linie ist alles, was nach getan werden mußwhile (...) {Daten zu bereinigen.

Der Modifizierer /e ermöglicht, dass die Ersatzseite als Code ausgewertet wird und nicht als Zeichenfolge in doppelten Anführungszeichen verwendet wird. Dort wird der übereinstimmende Teil der Linie (zwischen ") durch Komma getrennt und dann durch Leerzeichen verbunden, wodurch das Feld fixiert wird. Siehe den letzten Artikel unter "Suchen und ersetzen" in perlretut.

Der gesamte Code wurde mit mehreren Zeilen und mehreren Kommas im ungültigen Feld getestet.

+0

Bin gerade erst von vier Tagen weg. Also, würde die Regex-Linie anstelle der Teilstr-Linie verwendet werden? – BigRedEO

+0

@BigRedEO Es ist das einzige, was Sie brauchen, sobald die 'while' Schleife beginnt, diese eine Zeile. Ich habe das zur Antwort hinzugefügt. damit es vollständig ist. Beachten Sie, dass ich auch den Modifizierer '/ g' hinzugefügt habe, damit alle fehlerhaften Felder (mit doppelten Anführungszeichen) in einer Zeile korrigiert werden. Gib mir Bescheid. – zdim

+0

@BigRedEO Ich weiß nicht, ob Sie bemerkt haben, dass ich meine Antwort aktualisiert habe, um Ihre Frage zu klären. Es gibt jetzt drei Wege, um es unter verschiedenen Umständen zu lösen, alle getestet. Ich bin gespannt, was ist damit am Ende passiert? – zdim

Verwandte Themen