PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : zeilenweises Suchskript für Textdateien


gemo
10.09.2005, 12:41:20
Allgemein gefragt:
Wie kann ich ein Array $A zeilenweise mit einem Array $B mit Suchbegriffen durchsuchen lassen, so dass ich als Ergebnis ein Array $C erhalte, in dem nur die Zeilen enthalten sind, in denen die Suchbegriffe aus $B entweder alle (UND) bzw. einzeln (ODER) enthalten sind?


Das Skript, das ich derzeit versuche auf php-Beine zu stellen, habe ich bereits mit viel Zeit und Mühe als perl.pl-Script laufen (live kann man das unter Ag Freiraum und Vegetation (http://www.freiraumundvegetation.de/bibliographie.htm) sehen). Ich will das php-Skript dann später in eine neue, mit Joomla! (Mambo) verwaltete Seite integrieren. Das kann ich zwar jetzt auch per wrapper, aber da ist eine direkte php-Lösung doch eleganter. Ausserdem wollte ich immer schon mal in php reinschnüffeln.

Soweit.

Ich bin mit meinem Skript schon soweit, dass ich mehrere Begriffe suchen lassen kann. Zudem kann ich die Quelldatei in einem select-Feld auswählen. Die erste Zeile dient als Inhaltsbeschreibung und wird farblich anders dargestellt. Der gesuchte Begriff wird in der Ausgabe fett dargestellt.

Meine bisherige Arbeit sieht bislang folgendermassen aus:


<?
$suche= $_POST['suche'];
$suchbegr = explode(" ", $suche);

$auswahl= $_POST['auswahl'];

foreach ($auswahl as $datei) {

$zeilen=file($datei);
$i=count($zeilen)-1;
$header = $zeilen[0];
unset($zeilen[0]);

foreach ($suchbegr as $suchb){
foreach ($zeilen as $line) {
if (preg_match("/$suchb/",$line)){
$lin = str_replace($suchb, "<b>" .$suchb ."</b>", $line);
$result[]=$lin;
}

}

}


print "<p> </p><p><b>Im Verzeichnis: </b><font color=\"blue\">" .$header ."</font><br>";
print "wurden " .count($result) ." Beitr&auml;ge in " .count($zeilen). " Literaturangaben gefunden.</p>";
print "<hr>";
for($x=0;$x<count($result);$x++){
echo "<p>" .$result[$x] . "</p>";
}
}
?>



Was mir noch fehlt ist eine Verzweigung, die nach UND / ODER unterscheidet und die Suche entsprechend ändert. Im Formular habe ich hierzu ein weiteres select eingebaut, das zwischen "und" / "oder" unterscheidet.

Ausserdem liefert mein Skript bei mehreren Suchbegriffen auch mehrfach die gleichen Zeilen, wobei jeweils auch nur ein Suchbegriff fett-hervorgehoben ist.

In dem Perl-skript habe ich das mit Hilfe einiger Forumsmitgliedern (allen sei herzlich gedankt) wirklich elegant und mit wenigen Zeilen Code hinbekommen. Nur kann ich das nicht für mich in php übersetzen.

in perl sieht der zentrale Suchteil dann so aus:



if ($BOOL eq 'oder'){
foreach $text (@TEXTS) {
if($line =~ /$text/){
$line=~ s/$text/<b>$text<\/b>/g; # Highligt the text found

&result;

}
}
}

elsif ($BOOL eq 'und') {
my $nomatch = @TEXTS;
for my $text (@TEXTS) {
my $textnometa = quotemeta ($text);
last unless $line =~ s/$textnometa/<b>$text<\/b>/g;
--$nomatch;
}
&result unless $nomatch;
}



Mir kommt es so vor, als ob ich die brauchbarsten Möglichkeiten in php übersehen habe und deshalb eine einfache und knappe Methode übersehen habe.

Gruß
Georges

xabbuh
10.09.2005, 19:48:23
Wie kann ich ein Array $A zeilenweise mit einem Array $B mit Suchbrgriffen durchsuchen lassen, so dass ich als Ergebnis ein Array $C erhalte, in dem nur die Zeilen enthalten sind, in denen die Suchbegriffe aus $B entweder alle (UND) bzw. einzeln (ODER) enthalten sind?
Ermittel doch die Unterschiede zwischen $A und $B mittels array_diff() (http://www.php.net/array_diff).

gemo
11.09.2005, 00:17:16
Vielleicht habe ich mich nicht klar genug ausgedrückt, aber das Array $A besteht aus ganzen Sätzen (eine Literaturangabe je Zeile, die aus einer Textdatei mit file() ausgelesen wurde). Nun möchte ich als Ergebnis (am liebsten ein Array $C) nur jene Zeilen haben, in denen eines oder alle Wörter (Suchbegriffe in Array $B) vorkommen.

z.B.
$A beinhaltet die Zeilen:
1970: Asterix und die Goten (3. Abenteuer in Frankreich, 1961)
1971: Asterix bei den Briten
1971: Asterix und die Normannen
1971: Asterix als Legionär
1972: Asterix und der Arvernerschild
1972: Asterix bei den olympischen Spielen


$B enthält (aus einem Suchtextfeld ausgelesen und in einzelne Wörter zerlegt)die Suchbegriffe:
'Asterix'
'Legionär'

Bei Verwendung der ODER-Suche soll das 6 Treffer ($C) ergeben:
1970: Asterix und die Goten (3. Abenteuer in Frankreich, 1961)
1971: Asterix bei den Briten
1971: Asterix und die Normannen
1971: Asterix als Legionär
1972: Asterix und der Arvernerschild
1972: Asterix bei den olympischen Spielen

Bei Verwendung der UND-Suche ergibt das natürlich nur mehr 1 Treffer ($C):
1971: Asterix als Legionär

Eigentlich eine einfache Sache, die ich wie gesagt mit Verschachtelungen,... etc. bislang nicht hinbekomm. Dabei funktioniert doch jede der milionenfach operierenden Suchmaschinen so.

Wie gesagt im bisherigen perl-Skript läuft das tadellos, aber in php hab ich das noch nicht hinbekommen.

Für eine Hilfe wäre ich da sehr dankbar.
Mit array_diff() scheint mir das allerdings nicht zu funktionieren.

Gruß
Georges

xabbuh
11.09.2005, 12:03:24
Achso, dann verstehe ich dein Problem.

Versuche es doch mal so:

<?php
// $subjects ist ein Array, das die Suchbegriffe enthält
// $file enthält den Pfad zur Datei
// $matches ist ein Array, in das die gefundenen Zeilennummern geschrieben werden
// $type enthält die Art, wie gesucht wird (AND bzw. OR)

$matches = array();
$content = file($file);

foreach($content as $lineno => $line) {
switch($type) {
case 'AND':
$foundAll = true;
foreach($subjects as $subject) {
// wenn einer der Suchbegriffe nicht gefunden wurde, abbrechen
if(strpos($line, $subject) === false) {
$foundAll = false;
break;
}
}
if($foundAll === true)
$matches[] = $lineno;
break;
case 'OR':
foreach($subjects as $subject) {
if(strpos($line, $subject) !== false) {
$matches[] = $lineno;
break; // nach dem ersten Treffer in dieser Zeile können wir die Suche abbrechen
}
}
break;
}
}
?>

gemo
11.09.2005, 17:29:36
Erst mal einen ganz großen Dank xabbuh
Das funktioniert schon ganz gut, ich habe den Code entsprechend angepasst und ergänzt.
Ausserdem musste ich doppelte Zeilen, wenn etwa zwei Suchbegriffe in der gleichen Zeile vorkommen mit array_unique(); entfernen.
Das ganze sieht jetzt so aus:


<?
// $subjects ist ein Array, das die Suchbegriffe enthält
// $file enthält den Pfad zur Datei
// $matches ist ein Array, in das die gefundenen Zeilennummern geschrieben werden
// $type enthält die Art, wie gesucht wird (AND bzw. OR)

$search= $_POST['search'];
$subjects = explode(" ", $search);
$files= $_POST['files'];
$type= $_POST['boolean'];


foreach ($files as $file) {

$matches = array();
$content = file($file);
$header = $content[0];// first line used as header
unset($content[0]);

foreach($content as $lineno => $line) {
switch($type) {
case 'AND':
$foundAll = true;
foreach($subjects as $subject) {
// wenn einer der Suchbegriffe nicht gefunden wurde, abbrechen
if(strpos($line, $subject) === false) {
$foundAll = false;
break;
}
}
if($foundAll === true)
$matches[] = $lineno;
$matches = array_unique($matches);

break;
case 'OR':
foreach($subjects as $subject) {
if(strpos($line, $subject) !== false) {
$matches[] = $lineno;
// break; // nach dem ersten Treffer in dieser Zeile können wir die Suche abbrechen
}
}
$matches = array_unique($matches);
break;
}
}

print "<br>";
print "<p><b>Im Verzeichnis: </b><font color=\"blue\">" .$header ."</font><br>";
if (count($matches)==1){
print "wurde " .count($matches)." Beitrag in " .count($content). " Zeilen gefunden.</p>";
}
else {
print "wurden " .count($matches)." Beitr&auml;ge in " .count($content). " Zeilen gefunden.</p>";
}
print "<hr>";
for($x=0;$x<count($matches);$x++){
echo "<p>" .$matches[$x] . "</p>";
}
}

?>




Ich erhalte natürlich mit strpos() 'nur' die Zeilennummern, möche für die Ausgabe natürlich den entsprechenden Text sehen. Deshalb hatte ich ja mit (preg_match("/$subject/",$line)) gearbeitet. Müsste doch eigentlich genau so funktionieren?
Wie bekomme ich jetzt aus $matches wieder den Inhalt aus $content???
Zudem möchte ich in der Ausgabe die Suchbegriffe in dem ausgegebenen Text hervorheben.

Gruß
Georges

xabbuh
11.09.2005, 17:38:44
Ich erhalte natürlich mit strpos() 'nur' die Zeilennummern, möche für die Ausgabe natürlich den entsprechenden Text sehen. Deshalb hatte ich ja mit (preg_match("/$subject/",$line)) gearbeitet. Müsste doch eigentlich genau so funktionieren?
Mit strpos() wird in meinem Beispiel nur überprüft, ob der Suchbegriff in der Zeile enthalten ist. Die entsprechende Position wird gar nicht weiter ausgewertet. Zwar funktioniert es mit preg_match() ebenfalls, allerdings sollte man reguläre Ausdrücke, wo es geht, vermeiden, um keine Performanceeinbußen dadurch zu erhalten.

Wie bekomme ich jetzt aus $matches wieder den Inhalt aus $content???
In dem Array $matches stehen jetzt alle Zeilennummern, auf die die Suchbegriffe zutreffen.
Also kannst du dir diese Zeilen so anzeigen lassen:

<?php
foreach($matches as $lineno) {
print $content[$lineno] . '<br />';
}
?>


Zudem möchte ich in der Ausgabe die Suchbegriffe in dem ausgegebenen Text hervorheben.
Das könntest durch str_replace() (http://www.php.net/str_replace) erreichen:

<?php
$search = array(
'Begriff1',
'Begriff2'
);
$replace = array(
'<b>Begriff1</b>',
'<b>Begriff2</b>'
);
$var = str_replace($search, $replace, $var);
?>

gemo
11.09.2005, 22:51:54
Besten Dank xabbuh für die Hinweise.
Ich habe das Skript jetzt komplett und es läuft nach meiner Beobachtung fehlerfrei. Sollte dennoch jemand Fehler finden, so teilt das doch bitte hier mit.
Nachdem ich wirklich lange nach einer derartigen elementaren und einfachen Suchfunktion für Flatfiles (.txt - für csv liesse sich das aber auch einfach anpassen) gesucht habe, will ich das Ergebnis hier allen zugänglich machen.

Viel Erfolg bei der Suche und einen Dank an alle, die Hinweise gegeben haben.


<?
/* $subjects ist ein Array, das die Suchbegriffe enthält
/ $file enthält den Pfad zur Datei
/ $matches ist ein Array, in das die gefundenen Zeilennummern geschrieben werden
/ $type enthält die Art, wie gesucht wird (AND bzw. OR)
/ 'search' ist der Name des Texteingabefeldes im Formular
/ 'files' ist der Name des Auswahlfeldes wobei die einzelnen Text-Dateien (.txt oder .csv)
mit ihrem Pfad folgendermassen übergeben werden:
<option value="./verzeichnis/Datei.txt">Name</option>
'boolean' Auswahlfeld mit den Optionen AND und OR:
<option value="AND">und</option>
<option value="OR">oder</option>

Viel Erfolg bei der Suche!
*/

$search= $_POST['search'];
if ("" == $search) {
print "<p><b><font color=\"red\">Sie haben kein Suchbegriff angegeben!</font></b></p>\n<br>\n<br>\n";
}
else
{

$search= $_POST['search'];
$subjects = explode(" ", $search);
$files= $_POST['files'];
$type= $_POST['boolean'];


foreach ($files as $file) {

$matches = array();
$content = file($file);
$header = $content[0];// first line used as header
unset($content[0]);

foreach($content as $lineno => $line) {
switch($type) {
case 'AND':
$foundAll = true;
foreach($subjects as $subject) {
// wenn einer der Suchbegriffe nicht gefunden wurde, abbrechen
if(strpos($line, $subject) === false) {
$foundAll = false;
break;
}
}
if($foundAll === true)
$matches[] = $lineno;
$matches = array_unique($matches);

break;
case 'OR':
foreach($subjects as $subject) {
if(strpos($line, $subject) !== false) {
$matches[] = $lineno;
break; // nach dem ersten Treffer in dieser Zeile können wir die Suche abbrechen
}
}
$matches = array_unique($matches);
break;
}
}

print "<br>";
print "<p><b>Im Verzeichnis: </b><font color=\"blue\">" .$header ."</font><br>";
if (count($matches)==1){
print "wurde " .count($matches)." Beitrag in " .count($content). " Angaben gefunden.</p>";
}
else {
print "wurden " .count($matches)." Beitr&auml;ge in " .count($content). " Angaben gefunden.</p>";
}
print "<hr>";

foreach($matches as $lineno) {
foreach ($subjects as $subject) {
$content[$lineno]= str_replace($subject, "<b>".$subject."</b>", $content[$lineno]);
}
print "<p>" .$content[$lineno] . "</p>";

}
}
}
?>


Gruß
Georges