Perlscript zum Vergleich von Dateien

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
gnude
Beiträge: 1569
Registriert: 14.09.2009 22:05:28
Kontaktdaten:

Perlscript zum Vergleich von Dateien

Beitrag von gnude » 05.10.2011 10:10:42

Hallo
ich versuche mich an einem Perl Script, das
zwei .csv Dateien vergleicht und diese dann zu einer neuen zusammenfügt.
Beispel:
File1:
A;B;C
D;E;F
G;H;I

File:
D;E;F
G;H;I
J;K;L

Im ersten Schritt würde es reichen,
wenn beide Dateien zu einer neuen zusammengefasst werden:
A;B;C
D;E;F
G;H;I
J;K;L

Nun habe ich mich schon selbst an dem Script probiert...aber als Perl Anfänger...
Bisher habe ich das:

Code: Alles auswählen

#!/usr/bin/env perl
print
   "Parsing address file\n"
   ;
open (my $in, "<", "xt_customers_addresses.csv") or die "file not found $!";
my $lines = <$in>;
while (<$in>)
 {
  print "$_";
 }
Der zeigt jetzt Zeilenweise die Datei an.
aber wie bekomm ich das Verglichen und zusammengesetzt?
Wie kann ich eine zweite Datei öffnen ?
Kann mir jemand helfen?

uname
Beiträge: 12419
Registriert: 03.06.2008 09:33:02

Re: Perlscript zum Vergleich von Dateien

Beitrag von uname » 05.10.2011 10:42:36

Also ich löse sowas mit Hashes.

Code: Alles auswählen

$wert{das ist eine Zeile}
wird gesetzt wenn in der Zeile "das ist eine Zeile" steht.

Und da ich keine Dateinamen mag nutze ich Übergabeparameter.

Code: Alles auswählen

./programm.pl datei1 datei2 datei3 ...

Code: Alles auswählen

#! /usr/bin/perl
for ($i=0;$i<=$#ARGV;$i++)
{ 
  open(FILE,"@ARGV[$i] ")  or die "Fehler bei Datei @ARGV[$i] \n";;
  while (<FILE>)
  { 
    $ent = $_;
    $wert{$ent} = 1;
  }
  close (FILE);
}
foreach $ent (keys %wert)
{
     print $ent;
}
Der Vergleich besteht im Prinizip im Setzen des Hash-Wertes. Mehrere Dateien kann man nur nacheinander mit dem gleichen Handler (<FILE>) öffnen. Man kann aber auch mehrere Dateien mit unterschiedlichen Datei-Handlern öffnen. Wäre aber bei meinem Ansatz nicht so toll.

Programm ist bestimmt nicht perfekt. Und wer schreibt nun eben einen awk-Einzeiler?

Benutzeravatar
gnude
Beiträge: 1569
Registriert: 14.09.2009 22:05:28
Kontaktdaten:

Re: Perlscript zum Vergleich von Dateien

Beitrag von gnude » 05.10.2011 11:08:07

Fehlermeldung:
Parsing address file
# CSV_XS ERROR: 2034 - EIF - Loose unescaped quote @ pos 46


Im Chat raten mir alle zu einer Funktion Text::CSV ...

Benutzeravatar
ThorstenS
Beiträge: 2875
Registriert: 24.04.2004 15:33:31

Re: Perlscript zum Vergleich von Dateien

Beitrag von ThorstenS » 05.10.2011 11:09:13

kein awk aber schön kurz mit comm aus Debiancoreutils:

Code: Alles auswählen

comm datei1 datei2 | sed -e 's#^[\t]*##'

uname
Beiträge: 12419
Registriert: 03.06.2008 09:33:02

Re: Perlscript zum Vergleich von Dateien

Beitrag von uname » 05.10.2011 11:16:29

Fehlermeldung:
Parsing address file
# CSV_XS ERROR: 2034 - EIF - Loose unescaped quote @ pos 46
Da hilft dann wohl nur das ganze Programm (zu posten | in die Tonne zu hauen).
Im Chat raten mir alle zu einer Funktion Text::CSV ...
Und welche Funktion aus der Bibliothek (die ich im übrigen nicht kenne) soll das Problem lösen?

Code: Alles auswählen

comm datei1 datei2 | sed -e 's#^[\t]*##'
Nett. Kannst du das sed-Zeug noch erklären.

Benutzeravatar
ThorstenS
Beiträge: 2875
Registriert: 24.04.2004 15:33:31

Re: Perlscript zum Vergleich von Dateien

Beitrag von ThorstenS » 05.10.2011 11:24:26

noch kürzer ist sort mit der Option unique (dabei kann man auch mehr als nur zwei Dateien angeben):

Code: Alles auswählen

sort -u datei1 datei2
Erklärung zu sed -e 's#^[\t]*##'
Der sed string sieht evtl. ungewohnt aus, weil ich nicht / sondern # als Trenner benutze. sed -e '/^[\t]*//' ist sicherlich geläufiger.
^ - matched auf den Zeilenanfang
[…]* - fasst das Vorkommen von einem oder mehreren … zusammen
\t - ein Tabulator
## - eine leere Ersetzung - sprich ich lösche

Damit werden ein oder mehrere führende Tabulatoren gelöscht, die comm ausgibt, um zu sehen aus welcher Datei die jeweilige Zeile kommt.

uname
Beiträge: 12419
Registriert: 03.06.2008 09:33:02

Re: Perlscript zum Vergleich von Dateien

Beitrag von uname » 05.10.2011 11:33:49

Interessant. Aber eigentlich ist "sort" mit "uniq" sowieso schöner.

Geht im übrigen auch so:

Code: Alles auswählen

cat datei1 datei2 |sort|uniq
sort datei1 datei2|uniq

Benutzeravatar
gnude
Beiträge: 1569
Registriert: 14.09.2009 22:05:28
Kontaktdaten:

Re: Perlscript zum Vergleich von Dateien

Beitrag von gnude » 05.10.2011 11:57:48

Das Problem ist wesentlich komplexer und ich glaube mit einem SED/AWK Script nicht mehr zu fassen.
Daher versuche ich mich nun systematisch mit Perl dem zu nähern.
Warum will ich kurz beschreiben damit ihr versteht was ich probiere:

Es geht um Datenabgleich zwischen einer Warenwirtschaft und einem Onlineshop.
Die von dem Hersteller mitgelieferten Schnittellen ermöglichen nur das Übernehmen von Bestellungen aus dem Shop in die Warenwirtschaft und das
Anlegen eines Kunden, wenn er noch nicht vorhanden ist.
Ich möchte nun aber die Kundendaten aus der Warenwirtschaft in den Shop übertragen.
Dazu erzeuge ich zwei Dateien in der Warenwirtschaft, eine mit den Kundennummern und Einstellungen, eine mit Kundennummern und Adressen.
Die sind vom Aufbau her schon passent mit der Tabelle die in der MySQL Datenbank im Shop steht.
Nun erzeuge ich die gleichen Dateien aus der SQL Datenbank im Shop. So das ich nun 2x Kundennummen habe (1x Shop, 1x Warenwirtschaft) und
2x Kundenadressen (1x Shop, 1x Warenwirtschaft).
Jetzt geht es an den ablgeich. Kunden werden in der Warenwirtschaft erfasst und kommen so neu dazu.
Die müssen im Shop eingepflegt werden.
Kunden haben sich im Shop angemeldet, aber noch nichts gekauft, und sind so nicht in der Warenwirtschaft. Sie müssen also im Shop verbleiben ohne überschrieben zu werden.
Dann gibt es Kunden, die haben etwas BEstellt und über die Bestellung wurde eine Kundennummer in der Warenwirtschaft angelegt.
Die müssen dann übernommen werden, allerdings muss das Kundenkennwort was sie sich ausgesucht haben übernommen werden.
Dann kann der "alte " Datensatz im Shop gelöscht und der neue aus der Warenwirtschaft übernommen werden.

Das alles möchte ich in den CSV Dateien wiederspiegeln, so das ich die Dateien vom Shop abzapfe, dann aus der Warenwirtschaft. Diese zusammenführe zu zwei neuen Dateien und
diese dann direkt in die MySQL Datenbank hochlade.

An diese Aufgabenstellung möchte ich mich nun Perlmässig herantasten und diese Lösen.
Ich glaube nicht das man mit AWK dieses Szenario noch abdecken kann.

uname
Beiträge: 12419
Registriert: 03.06.2008 09:33:02

Re: Perlscript zum Vergleich von Dateien

Beitrag von uname » 05.10.2011 13:04:47

Bevor du programmierst solltest du dir ein paar Schnittstellen (CSV-Dateien) definieren, die für den Export- und den Import geeignet sind. Nach dieser Definition kannst du ein paar Perl-Programme schreiben, die entsprechend die Dateien umwandeln. Am besten erst mal ein Bild malen.

Benutzeravatar
gnude
Beiträge: 1569
Registriert: 14.09.2009 22:05:28
Kontaktdaten:

Re: Perlscript zum Vergleich von Dateien

Beitrag von gnude » 05.10.2011 13:13:47

Die Schnittstellen, also das Aussehen der Übergabedateien steht bereits fest
und ist so definiert, das ich es direkt als csv in die mysql Datenbank einlesen kann.

Mir geht es im Moment darum die CSV Datei zu zerlegen.
Wie gesagt, Schritt für Schritt.

Mit Hilfe des Perl IRC Channels hab ich mich bis zu diesem Script vorgekämft,
das zeigt jetzt Zeile für Zeile an. Aber noch mit falschen Umlauten.
Nun hab ich schon verschiedene Werte für das Encoding probiert, allerdings ohne Erfolg.

Code: Alles auswählen

#!/usr/bin/env perl
use Text::CSV;
use strict; 
use warnings; 

print
   "Parsing address file\n"
   ;

my@rows;
my $csv = Text::CSV->new ( { binary => 1 } )
        or die "File not found: ".Text::CSV->error_diag ();

open my $fh, "<:encoding(ISO-8859-15)", "liste" or die "liste: $!";
 while ( my $row = $csv->getline ( $fh ) ) {

 my @members_of_the_row = @$row;
 print  @members_of_the_row;
print "\n"; 
 }

$csv->eof or $csv->error_diag();
close $fh;

Benutzeravatar
gnude
Beiträge: 1569
Registriert: 14.09.2009 22:05:28
Kontaktdaten:

Re: Perlscript zum Vergleich von Dateien

Beitrag von gnude » 05.10.2011 13:53:01

Mit dieser Konstruktion habe ich doch zwei Dateien ausgelesen,
und in einem Array abgelegt.
Die muss ich doch nun nur noch verlgeichen, richtig?

Code: Alles auswählen

#!/usr/bin/perl 
use strict;
use warnings;
use diagnostics;
use Text::CSV_XS;

 my $file = 'xt_customers_addresses.csv'; #csv file name

 my @rows; # array that will store csv values


 my $csv = Text::CSV_XS->new ({ binary => 1 }) or
     die "Cannot use CSV: ".Text::CSV->error_diag ();

#open file
 open my $FH, "<:encoding(ISO-8859-15)", "$file" or die "$file: $!";

#skip first n (10 in this case) lines
<$FH> for 1 .. 10;

#read file in while loop
# my $i = 1;
 while (my $row = $csv->getline ($FH) ) {
#     if ($i > 1) 
        {push @rows, $row;}
#     $i++;
     }

 $csv->eof or $csv->error_diag ();

#close file
 close $FH;

#print contents of @rows array
for my $print_rows (@rows)
        {
        print "@$print_rows\n";
        }

 my $file = 'xt_customers_addresses.csv'; #csv file name

 my @rows; # array that will store csv values


 my $csv = Text::CSV_XS->new ({ binary => 1 }) or
     die "Cannot use CSV: ".Text::CSV->error_diag ();





 my $file2 = 'xt_customers_addresses.txt'; #csv file name

 my @rows2; # array that will store csv values


 my $csv2 = Text::CSV_XS->new ({ binary => 1 }) or
     die "Cannot use CSV: ".Text::CSV->error_diag ();


#open file
 open my $FH2, "<:encoding(ISO-8859-15)", "$file2" or die "$file2: $!";

#skip first n (10 in this case) lines
<$FH2> for 1 .. 10;

#read file in while loop
# my $i = 1;
 while (my $row2 = $csv2->getline ($FH2) ) {
#     if ($i > 1) 
        {push @rows2, $row2;}
#     $i++;
     }

 $csv2->eof or $csv2->error_diag ();

#close file
 close $FH2;

#print contents of @rows array
for my $print_rows2 (@rows2)
        {
        print "@$print_rows2\n";
        }

Antworten