Schrift
[thread]11962[/thread]

Problem beim taggen einer Datei via zweiter Datei.



<< >> 7 Einträge, 1 Seite
efbe
 2008-06-02 22:19
#110648 #110648
User since
2008-06-02
2 Artikel
BenutzerIn
[default_avatar]
Hallo,

Ich (totaler Perlneuling) habe folgendes Problem:
Ich habe 2 Dateien. Eine enthaelt Nomina, Verben und die Anzahl wie haeufig diese miteinander vorkommen.
Hier ein Beispiel:

Zeit haben 26061
Zeit gehaben 4410
Zeit nehmen 3569
Zeit stehen 3443

Die ganze Datei hat ca. 170MB.

Die zweite Datei enthaelt ausschliesslich Verben.

Beispiel:

abgeben
abhalten
ablegen
ablehnen
abnehmen

Ich will jetzt die Verben in der ersten Datei mit Klammern[] markieren, die auch in der zweiten Datei vorkommen.
Ergebnis dieser Operation waere also zB

Zeit [abnehmen] 234

Mein Ansatz war bis jetzt der Folgende:
Zuerst die erste Datei in Spalten splitten... das funktioniert auch soweit.
Wenn ich dann allerdings nachsehen will ob das Wort in der 2. Datei vorkommt, gibt mir das Programm nichts mehr zurueck.

Meine Frage ist jetzt... was habe ich hier falschgemacht?
oder ist einfach mein kompletter Ansatz total bescheuert.
... in dem Fall... was waere ein guter Ansatz fuer mein Problem?

...ich sitze hier seit mehreren Tagen an diesem eigentlich ja simplen Problem und bin inzwischen total durch den Wind.
Bin also fuer jede Hilfe dankbar!

die Dateien habe ich auch mal ins Netz gestellt unter den folgenden Adressen:

http://kablooie.org/perl/tag.txt
http://kablooie.org/perl/koroesi.txt
http://kablooie.org/perl/svc_test_sdl.txt

Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#/usr/bin/perl
use strict 'vars';

{
my @words;
my $file_in;
my $file_test;
my @columns;
my @datafile;
my $line;
my @koroesi;
my %koroesi;
my $currentline;

$file_in = "svc_test_sdl.txt";
$file_test = "koroesi.txt";

open (INPUT, "$file_in") or die %!;
open (TESTFILE, "$file_test") or die %!;

@datafile = <INPUT>;
close (INPUT);

@koroesi = <TESTFILE>;
close (TESTFILE);
%koroesi = @koroesi;

{
foreach $line (@datafile){
        chomp $line;
        (@columns) = split (/ /,$line);

    $currentline = $columns[1];

        print "$currentline\n";

        if (exists($koroesi{$currentline}))
        {
                s/$currentline/\[$currentline\]/;
        }
}       
}
} 
Linuxer
 2008-06-02 22:49
#110649 #110649
User since
2006-01-27
3891 Artikel
HausmeisterIn

user image
Hier mein Vorschlag, um das beschriebene Problem zu lösen:

Der 1. Schritt liest die Liste der Verben ein, entfernt die Zeilenumbrüche und speichert die Verben als Hash-Keys mit undefinierten Values; Einen Hash-Key kann man einfacher auf Existenz prüfen als in einer Liste zu suchen, ob ein bestimmtes Element vorhanden ist.

Der 2. Schritt liest die Datendatei ein und trennt jede Zeile an den Whitespaces (Leerzeichen, Newlines, Tabs, etc.). Dadurch spart man das explizite Entfernen des Newlines (was ohnehin nicht notwendig wäre).
Als Stücke bleiben das Nomen, das Verb und die Anzahl übrig.
Ist das Verb definiert, wird stillschweigend davon ausgegangen, dass die anderen Daten entsprechend korrekt sein sollten.
Ist das Verb als Hash-Key vorhanden (und war somit in der Liste der Verben), wird die Ausgabe entsprechend erweitert; wenn nicht, wird die originale Zeile ausgegeben.

Soll die Ausgabe wieder in eine Datei, kann man die Ausgabeumleitung nutzen:

Code: (dl )
$ perl skript.pl > ausgabe.txt


Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/perl
# vi: set ts=4 sw=4 sts=2 et:
use strict;
use warnings;

# Author:   linuxer
# Date:      02. June 2008

# Description:
#       http://board.perl-community.de/thread/11962/#MSG0



my %verb;

# 1st step: read all verbs into a hash

{
    my $verb_file = 'koroesi.txt';

    open my $fh, '<', $verb_file or die "$verb_file: open(ro) failed: $!\n";

    # store all verbs in array;
    my @verbs = <$fh>;
    # remove all newlines
    chomp @verbs;
    # store all verbs as hash keys
    @verb{@verbs} = ();

    close $fh or die "$verb_file: close(ro) failed: $!\n";
}


# 2nd step: do the work
{
    my $check_file = 'svc_test_sdl.txt';

    open my $fh, '<', $check_file or die "$check_file: open(ro) failed: $!\n";

    while ( my $line = <$fh> ) {

        # split at whitespace (beware: newlines are whitespaces, so no chomp needed)
        my ( $noun, $verb, $number ) = split /\s+/, $line;

        # if $verb is defined, assume all variables are correctly filled
        if ( defined $verb ) {

            # is $verb a key in hash %verb?
            if ( exists $verb{$verb} ) {
                # print the modified data
                printf "%s [%s] %s\n", $noun, $verb, $number;
                # oder ohne printf
                #print $noun, " [$verb] ", $number, "\n";
            }
            # if not
            else {
                # print unmodified line
                print $line;
            }
        }
    }

    close $fh or die "$check_file: close(ro) failed: $!\n";

}

__END__
meine Beiträge: I.d.R. alle Angaben ohne Gewähr und auf Linux abgestimmt!
Die Sprache heisst Perl, nicht PERL. - Bitte Crossposts als solche kenntlich machen!
pq
 2008-06-02 23:04
#110650 #110650
User since
2003-08-04
12209 Artikel
Admin1
[Homepage]
user image
vorbildliche problembeschreibung =)
aber ich hab da ein paar anmerkungen. ein paar sachen kannst du schöner schreiben,
und fehler.
efbe+2008-06-02 20:19:06--
Code (perl): (dl )
use strict 'vars';

hier am besten:
Code (perl): (dl )
1
2
use strict;
use warnings;


Quote
Code (perl): (dl )
1
2
3
4
{
my @words;
my $file_in;
...

am besten deklariert man die variablen erst da, wo sie gebraucht werden, nicht alle
am anfang. das ist sauberer.
Quote
Code (perl): (dl )
1
2
open (INPUT, "$file_in") or die %!;
open (TESTFILE, "$file_test") or die %!;


Code (perl): (dl )
1
2
open INPUT, '<', $file_in or die $!;
open TESTFILE, '<', $file_test or die $!;


Quote
Code (perl): (dl )
%koroesi = @koroesi;


hier der fehler. der hash wird gefüllt mit jedem zweiten wort als schlüssel und den
anderen als wert. ausserdem musst du erst noch die zeilenenden entfernen.
Code (perl): (dl )
1
2
chomp @koroesi;
my %koroesi = map { ( $_ => 1) } @koroesi;


Quote
Code (perl): (dl )
        (@columns) = split (/ /,$line);

meinem geschmack nach zu viele klammern.
Code (perl): (dl )
my @columns = split / /, $line;
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
Linuxer
 2008-06-02 23:08
#110651 #110651
User since
2006-01-27
3891 Artikel
HausmeisterIn

user image
efbe+2008-06-02 20:19:06--
Hallo,
Mein Ansatz war bis jetzt der Folgende:
Zuerst die erste Datei in Spalten splitten... das funktioniert auch soweit.
Wenn ich dann allerdings nachsehen will ob das Wort in der 2. Datei vorkommt, gibt mir das Programm nichts mehr zurueck.

Meine Frage ist jetzt... was habe ich hier falschgemacht?
oder ist einfach mein kompletter Ansatz total bescheuert.
... in dem Fall... was waere ein guter Ansatz fuer mein Problem?

Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#/usr/bin/perl
use strict 'vars';

{
my @words;
my $file_in;
my $file_test;
my @columns;
my @datafile;
my $line;
my @koroesi;
my %koroesi;
my $currentline;

$file_in = "svc_test_sdl.txt";
$file_test = "koroesi.txt";

open (INPUT, "$file_in") or die %!;
open (TESTFILE, "$file_test") or die %!;

@datafile = <INPUT>;
close (INPUT);

@koroesi = <TESTFILE>;
close (TESTFILE);
%koroesi = @koroesi;

{
foreach $line (@datafile){
        chomp $line;
        (@columns) = split (/ /,$line);

    $currentline = $columns[1];

        print "$currentline\n";

        if (exists($koroesi{$currentline}))
        {
                s/$currentline/\[$currentline\]/;
        }
}       
}
} 

Anmerkungen zu Deinem Skript:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
:o( keine "warnings"
:o( warum "strict" auf 'vars' eingeschränkt

:o) (zeile 18/19) open auf misserfolg geprüft
verbesserung: 3-argument-open: open HANDLE, '<', $filename

:o( (zeile 26) %hash = @array; # boese
mit @array = ( 1,2,3,4 ) ergibt das %hash = ( 1=>2, 3=>4 );
:o( ausserdem stecken die Zeilenumbrüche noch in der Liste der Verben

:o| (zeile 37) wird nicht klappen, weil hier getestet wird,
ob das Verb ohne Umbruch als Hash-Key existiert... (s.o.)

:o( (zeile 39) s/// wird hier auf $_ angewendet, was aber nicht definiert
sein dürfte; die Zeile steckt in $line

:o| Die Maskierung der [] in der Ersetzung ist nicht notwendig
meine Beiträge: I.d.R. alle Angaben ohne Gewähr und auf Linux abgestimmt!
Die Sprache heisst Perl, nicht PERL. - Bitte Crossposts als solche kenntlich machen!
topeg
 2008-06-02 23:24
#110652 #110652
User since
2006-07-10
2611 Artikel
BenutzerIn

user image
Nach den anderen Vorschlägen meiner:
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/perl

use strict;
use warnings;

my $file_in = 'svc_test_sdl.txt';
my $file_out = 'svc_test_sdl.out.txt';
my $file_test = 'koroesi.txt';

my %koroesi;

# Datei öffnen
# (wenn es nicht geht enthält $! die Fehlermeldung)
open(my $fh,'<',$file_test) or die "konnte $file_test nicht öffnen! ($!)\n";
# Datei zeilenweise einlesen
while(my $verb=<$fh>)
{
  # "\n" am ende entfernen,
  # wenn vorhanden
  chomp($verb);
  # Hash füllen
  $koroesi{$verb}=1;
}
close($fh);

# Wenn man ein Array nimmt um alle Zeilen zu speichern,
# dann verbraucht man auch eine menge Speicher.
# Da ist es besser die datei Zeilenweise zu bearbeiten.
# Eingabedatei öffnen
open(my $fh_in,'<',$file_in) or die "konnte $file_in nicht öffnen! ($!)\n";
# Ausgebedatei öffnen
open(my $fh_out,'>',$file_out) or die "konnte $file_out nicht öffnen! ($!)\n";
while(my $line=<$fh_in>)
{
  chomp($line);
  # Zeile zerlegen
  my($noun, $verb, $number)=$line=~/^(.+?)\s+(.+?)\s+(.+?)$/;
  # so geht es natürlich auch:
  # my($noun, $verb, $number)=split(/\s+/,$zeile);
  
  # wenn das wort im Hash als Schlüssel existiert
  if(exists($koroesi{$verb}))
  {
    # dann neue Zeile erzeugen
    $line = "$noun [$verb] $number";
  }
  # das ganze in die neue Datei schreiben
  print $fh_out $line."\n";
}
# Ausgabedatei schließen
close($fh_out);
# Eingabedatei schließen
close($fh_in);

Wie schon gesagt bei großen Dateien ist es besser Zeilenweise zu arbeiten.
Nutze "use strict;" und "use warnings;"
Zeilenumbrüche entfernen, sonst kann der Vergleich nicht klappen!
Ich halte es bei 170MB Daten nicht für sinnvoll die Ausgabe in die Konsole zu machen. Das direkte Zurückschreiben in eine Datei ist da sinnvoller.
Linuxer
 2008-06-02 23:28
#110653 #110653
User since
2006-01-27
3891 Artikel
HausmeisterIn

user image
Quote
Ich halte es bei 170MB Daten nicht für sinnvoll die Ausgabe in die Konsole zu machen. Das direkte Zurückschreiben in eine Datei ist da sinnvoller.


Prinzipiell geb ich Dir recht. Mir ging es in dem Skript nur um die Beispieldaten und die sind erheblich übersichtlicher ;o)) Der Sinn entscheidet sich - denk ich - letztenendes darin, was mit den generierten Daten weiter passieren soll.

Anmerkung meinerseits:
Aktionen auf den schreibenden Handle ebenfalls auf Erfolg prüfen (print / close), falls mal die Platte voll ist, o.ä.
meine Beiträge: I.d.R. alle Angaben ohne Gewähr und auf Linux abgestimmt!
Die Sprache heisst Perl, nicht PERL. - Bitte Crossposts als solche kenntlich machen!
efbe
 2008-06-03 00:17
#110655 #110655
User since
2008-06-02
2 Artikel
BenutzerIn
[default_avatar]
ja wahnsinn... vielen dank euch allen fuer die schnellen antworten! und tolle tips fuer mich als anfaenger :)

dankeschoen!

<< >> 7 Einträge, 1 Seite



View all threads created 2008-06-02 22:19.