Suche was zum Suchen: 2 Textdateien

Hallo.

Habe folgende konkrete Aufgabenstellung.
Vielleicht gibt es schon eine Implementierung in Perl?

Gegeben seien zwei ASCII-Textdateien:
a) eine kleine sortierte Wortliste und
b) ein großes und umfangreiches Dokument.

Es soll eine dritte Datei erstellt werden,
in der alle Wörter aus a), die auch in b) vorhanden sind,
ausgegeben werden.
Nützlich wäre auch die Angabe der Textposition
bzw. die Häufigkeit.

Danke!

Gruß,
SAB

Hallo Sabine,

da wirst du wohl selbst ein paar Zeilen programmieren muessen (wenn dir die Funktionen von grep oder fgrep) nicht ausreichen.

Die einfache Lösung:

fgrep -f 

Die Langversion (als schlampiger Pseudocode):

Patternfile einlesen (und im hash %suchworte speichern)

while ()
{
 $\_ in Worte zerlegen;
 foreach $wort (alle Worte der Zeile)
 {
 if (exists $suchworte{$wort})
 {
 $found{$wort}++;
 }
 }
}

Ausgabe %found # welches Word wurde wie oft gefunden...

Rest als Hausaufgabe (das wars doch wohl, oder?)

gruss
bernhard

Hallo Sabine

Gegeben seien zwei ASCII-Textdateien:
a) eine kleine sortierte Wortliste und
b) ein großes und umfangreiches Dokument.
Es soll eine dritte Datei erstellt werden,
in der alle Wörter aus a), die auch in b) vorhanden sind,
ausgegeben werden. Nützlich wäre auch die Angabe der
Textposition bzw. die Häufigkeit.

OK. Bernhard hat ja schon einige Hinweise
dazu gegeben.

Vielleicht gibt es schon eine Implementierung in Perl?

Dazu braucht man keine „Implementierung in Perl“
zu haben. Das Problem ist relativ einfach zu
formulieren.

(Da Du hier schreibst, nehme ich an, dass
Du erstmal nur eine Teilaufgabe lösen möchtest).

Falls man den gesamten Text in einem
Skalar $fulltext und alle Suchworte
hintereinander in einem Skalar $wordlist
hätte, würde folgende Schleife die
(Kern-)Aufgabe erfüllen:

 for( split /\s+?/, $<u>wordlist</u> ) {
 while( $<u>fulltext</u> =~ m/($\_)/sgi ) {
 print ++$counts-\>{$\_}, ". $1 at pos $-[0] to $+[0] ($\_)\n";
 }
 }

Das würde jeweils die Treffer-Positionen
(Wortanfang bis Wortende) und die Anzahl
der Treffer $counts->{SUCHWORT} liefern.

Als Suchwort könnte hierbei auch ein regulärer
Ausdruck verwendet werden, z.B.: Sabi\w*?\b
(findet alle Worte, die mit Sabi anfangen),
oder eben einfach ‚Sabine‘.

Ich will Dir natürlich nicht den Spass nehmen,
die Aufgabe komplett zu lösen, also alles mit

  • Datei-1 einlesen nach $fulltext,
  • Datei-2 einlesen nach $wordlist,
  • Datei-3 ausgeben gefundener Treffer und Häufigkeiten.

Aber das kannst Du nun hinbekommen :wink:

Grüße

CMБ

Hi Semjon,

@+ und @- kannte ich noch nicht persönlich (noch nie explizit verwendet). Guter Trick!

Je nachdem, wie gross der Gesamttext ist, kann das Einlesen in einen einzigen Skalar Probleme machen (aber das brauche ich dir wahrscheinlich nicht zu erzählen :wink:.

gruss
bernhard

Hallo.

Vielen Dank für die Hilfe/Hinweise!

MfG
SAB

Hallo Bernhard

@+ und @- kannte ich noch nicht persönlich (noch nie explizit
verwendet). Guter Trick!

Bin ich auch nur durch Zufall drauf gestossen,
ist imho recht selten dokumentiert.
(z.B. hier http://perldoc.perl.org/perlreref.html)

Je nachdem, wie gross der Gesamttext ist, kann das Einlesen in
einen einzigen Skalar Probleme machen

Also ich mache das hier immer mit
dem „{ local $/ …}“-Idiom, also z.B.:

 open (FH, myFulltext) or die "$!";
 my $fulltext = do { local $/; () };
 close FH;

Alternativ wäre noch Damian’s „use Perl6::Slurp“
zu verwenden: http://search.cpan.org/~dconway/Perl6-Slurp/,
welches folgende „prägnante“ Schreibweise
ermöglicht:

 $file\_contents = slurp 'Ich habe, als ich hier ein wenig rumprobierte,
den Nietzsche-Volltext (14MB, 343000 Zeilen)
genommen :wink:. Da gab es zumindest keine Probleme
mit der Größe.

Wenn es wirklich sehr grosse Dateien sind, muss
man sich schon generell was überlegen, um das
System nicht totzuswappen. Aber dass es hierbei
darum geht, wage ich zu bezweifeln :wink:

Übrigens: eine Alternative (wenn keine
regulären Ausdrücke als Suchbegriffe
vorkommen) wäre noch, den gesamten
Volltext in einen Hash (mit eben jedem
"Wort" des Textes als key) zu ziehen,
deren Values dann ein anonymes Array
ihrer Positionen sind, also etwa so:

     my (%lookup);
    
     while( $fulltext =~ m/\b(\w+)/og ) {
     push @{ $lookup{$1} }, $-[1]; # merke positionen aller Worte
     } # und damit auch deren Anzahl
     print "hash build up o.k. (" . %lookup . ") so far\n";


Aber das ist a\*\*\*-langsam. Dauert hier ca.
6 Sekunden (beim Nietzsche-Volltext) auf
einem Athlon 3400+ :-[

Aber dafür ist dann das Suchen lediglich
ein "Nachschlagen im Hash" und auch bei
sehr grossen Suchlisten praktisch kostenlos.

     for my $word (split /\s+?/, $wordlist) {
     if ( (my $hit=$lookup{$word}) ) {
     print "$word\t:", scalar @{$hit}, "x at ${$hit}[0] ...\n";
     }
     }

Muss man halt abwägen.

Grüße

CMБ

Hi Semjon,

den Nietzsche-Volltext (14MB, 343000 Zeilen)
genommen :wink:. Da gab es zumindest keine Probleme
mit der Größe.

mit Sicherheit richtig. Bei grossen Dateien denke ich immer an
> Gigabyte (muss ich zur Zeit ständig durchfräsen, sind aber auch
keine Texte :smile:

Aber das ist a***-langsam. Dauert hier ca.
6 Sekunden (beim Nietzsche-Volltext) auf
einem Athlon 3400+ :-[

Die alte Regel:
man kann höchstens eins bekommen, schnell oder wenig Speicherverbrauch (im schlimmsten Fall keins von beiden).

Trotzdem: wieder was dazugelernt.

gruss
bernhard