[PHP] Tekstbestand uitlezen en tonen op bepaalde criteria

Pagina: 1
Acties:
  • 1.076 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

  • DeepFreeze.NL
  • Registratie: April 2006
  • Laatst online: 02-03 08:01
Ik heb een tekstbestand waarin meer dan 1000 regels in staan. Nu wil ik een functie maken waarmee aan de hand van de opgegeven url een bepaald aantal regels worden getoond.

Wanneer een bezoeker bijvoorbeeld index.php?letter=a bezoekt dan moet hij alle regels uit het tekstbestand te zien krijgen die beginnen met de letter a of A.

Ik heb nog geen functie ingebouwd die ook kijkt naar de hoofdletter, want ik krijg het al niet eens voor elkaar dat de regels beginnend met kleine letters worden getoond.

Mijn kennis van PHP is basic. Ik heb een hele tijd naar oplossingen gezocht, maar ik kon niets vinden dat mij het probleem duidelijk kan maken. Het probleem zit hem ergens in de while functie, de pagina blijft namelijk laden, maar er wordt niets getoond.


PHP:
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
function getPlaylist() {
    $request = parse_url($_GET['letter']);
    $path = rtrim($request['path'], '/');
    $urlletter = $_GET['letter'];
    
    function filter($letter) {
        $filename = file ("files/playlist.txt");
        $rows = count ($filename);
        //echo "Number of songs: ".$rows."<br /><br />";
        $fp = fopen($filename, "r");
        $currentLine = fgets($fp);
        
        while (!feof($fp)) {
            // process current line
            $currentLine = fgets($fp);
            $firstLetter = substr($currentLine, 0, 1);
            for ($i = 0; $i < $rows; $i++) {
                if ($firstLetter == $letter) {
                    echo "test: ".$currentLine."<br />";
                }
            }
        }
        fclose($fp);
        //echo "letter = ".$letter."<br />";
        //echo "firstLetter = ".$firstLetter."<br />";
        //echo "currentLine = ".$currentLine."<br />";
    }
    filter($urlletter);

Acties:
  • 0 Henk 'm!

  • LPEspecial
  • Registratie: September 2004
  • Laatst online: 15-07-2024
Een van de eerste problemen die ik zie is dat je lijnen waarin meerdere te vinden letters staan ook meerdere keren geprint worden. Bovendien wordt de eerste regel altijd genegeerd.

In de basis zou onderstaande moeten ongeveer moeten werken (heb het echter niet geprobeerd).

PHP:
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
function filter($letter) {
    $filename = file ("files/playlist.txt");
    $rows = count ($filename);
    echo "Number of songs: ".$rows."<br><br>";
    $fp = fopen($filename, "r");
    $currentLine = fgets($fp);
    
    while (!feof($fp)) {
        // process current line
        $pos = strpos($currentline, $letter);
        if ($pos === false) {
            // Not found
        } else {
            echo "test: ".$currentLine."<br>";
        }
        // Get next line
        $currentLine = fgets($fp);
    }
    fclose($fp);
}

function getPlaylist() {
    $request = parse_url($_GET['letter']);
    $path = rtrim($request['path'], '/');
    $urlletter = $_GET['letter'];
    
    filter($urlletter);
}


Zoek eens op een goede reference site zoals:
http://nl3.php.net/manual/en/function.strpos.php

LPEspecial


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 02:21

Janoz

Moderator Devschuur®

!litemod

Ik zie meerdere fouten in je script en je zou daarom eens even moeten nadenken over de volgende dingen:
Wat levert de functie file op en wat is het type daarvan?
Waarom stop je dit dan in een variabele die filename heet?
Als het daadwerkelijk de naam was, waarom werkt count dan blijkbaar?
En tot slot kom je dan tot de conclusie dat de fopen die een filenaam verwacht helemaal nergens op slaat.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • LPEspecial
  • Registratie: September 2004
  • Laatst online: 15-07-2024
Janoz schreef op vrijdag 07 december 2007 @ 11:18:
Ik zie meerdere fouten in je script en je zou daarom eens even moeten nadenken over de volgende dingen:
Wat levert de functie file op en wat is het type daarvan?
Waarom stop je dit dan in een variabele die filename heet?
Als het daadwerkelijk de naam was, waarom werkt count dan blijkbaar?
En tot slot kom je dan tot de conclusie dat de fopen die een filenaam verwacht helemaal nergens op slaat.
Daar heb ik nog eens overheen gelezen, lol.. Zal me leren om 's-ochtens vroeg te reageren.

[ Voor 3% gewijzigd door LPEspecial op 07-12-2007 11:20 ]

LPEspecial


Acties:
  • 0 Henk 'm!

  • DeepFreeze.NL
  • Registratie: April 2006
  • Laatst online: 02-03 08:01
Janoz schreef op vrijdag 07 december 2007 @ 11:18:
Ik zie meerdere fouten in je script en je zou daarom eens even moeten nadenken over de volgende dingen:
Wat levert de functie file op en wat is het type daarvan?
Waarom stop je dit dan in een variabele die filename heet?
Als het daadwerkelijk de naam was, waarom werkt count dan blijkbaar?
En tot slot kom je dan tot de conclusie dat de fopen die een filenaam verwacht helemaal nergens op slaat.
file geeft de inhoud van het bestand. De benaming klopt inderdaad niet en had $file moeten zijn.
Dit heeft trouwens geen invloed op het script. Het inlezen van het bestand gaat goed, het tonen van alle regels die beginnen met bijvoorbeeld de letter "a" gaat fout.

Acties:
  • 0 Henk 'm!

  • MacWebber
  • Registratie: September 2000
  • Niet online
Het inlezen van het bestand gaat niet alleen goed, het gaat zelfs te goed. Je doet het namelijk twee keer. Een keer met de functie file() en vervolgens nog eens met fopen() en fgets().

Dat is dus wat overdone.
Gebruik het resultaat van de file() functie (dat is een array van regels, daar kun je dus doorheen wandelen). Combineer dat met de strpos() functie die LPEspecial introduceert en je moet een heel eind kunnen komen.

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Je doet echt de raarste dingen met een aantal van PHP's meest basale functies. Lees aub eerst eens over alle door jou gebruikte functies in de PHP docs.

{signature}


Acties:
  • 0 Henk 'm!

  • DeepFreeze.NL
  • Registratie: April 2006
  • Laatst online: 02-03 08:01
MacWebber schreef op vrijdag 07 december 2007 @ 11:36:
Het inlezen van het bestand gaat niet alleen goed, het gaat zelfs te goed. Je doet het namelijk twee keer. Een keer met de functie file() en vervolgens nog eens met fopen() en fgets().

Dat is dus wat overdone.
Gebruik het resultaat van de file() functie (dat is een array van regels, daar kun je dus doorheen wandelen). Combineer dat met de strpos() functie die LPEspecial introduceert en je moet een heel eind kunnen komen.
Bedankt! Dit is nou de hulp die ik nodig had :) !
De oplossing:
PHP:
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
function getPlaylist() {
    // verdeel de URL in zijn verschillende componenten
    $request = parse_url($_GET['letter']);
    // we nemen alleen het pad, zonder de eventuele slash op het einde
    $path = rtrim($request['path'], '/');
    $urlletter = $_GET['letter'];
    
    function filter($letter) {
        $array = file ("files/playlist.txt");
        $rows = count ($array);
        
        echo "Number of songs: ".$rows."<br />";
        echo "Letter: ".$letter."<br /><br />";
        
        foreach ($array as $value) {
            // process current line 
            $pos = strpos($value, $letter); 
            if ($pos === false) { 
                // Not found 
            } else { 
                echo $value."<br>"; 
            }
        }
        fclose($fp);
    }
    filter($urlletter);
}

[ Voor 3% gewijzigd door DeepFreeze.NL op 07-12-2007 12:05 ]


Acties:
  • 0 Henk 'm!

  • MacWebber
  • Registratie: September 2000
  • Niet online
Nou, dan ga je nu ipv strpos eens kijken naar stripos...

(Die had je natuurlijk zelf even in het php handboek kunnen opzoeken...)

Acties:
  • 0 Henk 'm!

  • MacWebber
  • Registratie: September 2000
  • Niet online
Je doet overigens ook nog een fclose($fp) terwijl je geen fopen() meer doet. Ook niet echt sjiek ;)

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
En $path en $request worden ook nergens voor gebruikt (was reeds in topicstart al zo). Een get parameter aan parse_url meegeven slaat sowieso al als een tang op een varken, 1 get param maakt nog geen url. En als je de param toch al hebt, hoef je ook niet te parsen. :P

{signature}


Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
Ben ik nu de enige die vooral het functie in functie ontwerp zo creatief vindt? :+

Ik zou het trouwens iets anders aanpakken:
PHP:
1
2
3
4
5
6
7
8
9
10
function getPlayList() {
  $request  = $_GET['letter'];
  $playList = file('files/playlist.txt');
  
  echo 'Number of songs: ' . sizeof($playList)   . '<br />';
  echo 'Letter: '          . $request            . '<br />';

  foreach($playList as $line)
    stripos($line, $request) === 0 ? print($line . '<br />') : false;
}


Eventueel zonder '=== 0' als je op string in string wilt zoeken, zoals je het nu omschreef wou je alleen die regels waarin de zoekterm aan het begin staat, maar de code die je post laat voor zover ik kan zien ook regels zien waarin de zoekterm alleen maar voorkomt :)

Ik zou je willen aanraden wat kritischer naar je eigen code te kijken: zoek op wat elke regel doet, en vraag je af of hij wel nodig is. Je hebt nu redelijk wat dubbele en overbodige code, dat werkt niet alleen vertragend maar is ook verwarrend en onduidelijk. Een script als wat jij zoekt hoeft heus geen gigantische functie te zijn :)

[ Voor 5% gewijzigd door FragFrog op 07-12-2007 18:48 . Reden: Echo verandert in een print, ik weet wat voor miereneukers anders gaan zeuren over de echo false :P ]

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • Bitage
  • Registratie: April 2006
  • Laatst online: 19-05-2024
PHP:
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
<?php
function getPlaylist()
{

  $data = file_get_contents('playlist.txt');
  $data = explode("\n",$data);

  echo "Totaal aantal nummers: <b>".count($data)."</b><br>\n";
  echo "Zoeken op: <b>".$_GET['letter']."</b><br><br>\n\n";

  foreach($data as $song)
  {
    // Gebruikt strtolower(); voor case-insensitive search
    if(substr(strtolower($song),0,strlen($_GET['letter'])) == strtolower($_GET['letter']))
    {
      echo $song."<br>\n";
      $num = $num + 1;
    }

  }

  echo "<br>Totaal aantal gevonden nummers: <b>".$num."</b>";

}

getPlaylist();

?>


Zoekt zelfs niet alleen op beginletter, maar op meerdere beginletters.
Dus stel, playlist.txt
code:
1
2
3
4
5
aapje
beestak - hoofd
gehkloop
uisuap
bhghas


Dan zal ?letter=geh dit geven
code:
1
2
3
4
5
6
Totaal aantal nummers: 5
Zoeken op: geh

gehkloop

Totaal aantal gevonden nummers: 1


Zou zou ik het aanpakken (al zou ik iig een MySQL DB gebruiken, maargoed)

[ Voor 25% gewijzigd door Bitage op 07-12-2007 18:41 ]


Acties:
  • 0 Henk 'm!

  • Grijze Vos
  • Registratie: December 2002
  • Laatst online: 28-02 22:17
FragFrog schreef op vrijdag 07 december 2007 @ 18:22:
Ben ik nu de enige die vooral het functie in functie ontwerp zo creatief vindt? :+
Dat hoeft niet eens bad practice te zijn hoor. Bijvoorbeeld als je met third-party libs werkt die ook generieke namen gebruiken voor ietwat generiekere functies, dan kun je zo die functie in de local scope definieren.

Ik gebruik het zelf ook wel eens als ik een compare functie voor een sort ofzo definieer. Blijft dan gewoon netjes in die scope waar het nodig blijft, en niet daarbuiten. (Deze argumentatie valt natuurlijk weg als je OOP programmeert.)

Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info


Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
Grijze Vos schreef op vrijdag 07 december 2007 @ 18:46:
(Deze argumentatie valt natuurlijk weg als je OOP programmeert.)
Dat zal het zijn ;)

In PHP4 heb je trouwens sowieso wel een goed punt aangezien classes daarin weinig anders zijn dan functies met een andere call :+

//edit @ Bitage: als je je straks afvraagt waarom je code niet werkt op een mac en wat je beter zou kunnen doen, kijk dan eens naar file :)

En kijk dan gelijk ook even naar stristr, stripos en andere case-insensitive string compares ;)

[ Voor 30% gewijzigd door FragFrog op 07-12-2007 18:51 ]

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • Bitage
  • Registratie: April 2006
  • Laatst online: 19-05-2024
FragFrog schreef op vrijdag 07 december 2007 @ 18:49:
//edit @ Bitage: als je je straks afvraagt waarom je code niet werkt op een mac en wat je beter zou kunnen doen, kijk dan eens naar file :)

En kijk dan gelijk ook even naar stristr, stripos en andere case-insensitive string compares ;)
Euw, heb verder geen ervaring met Mac machines *shame* Zijn wel mooi, maar das een ander forum :9

Acties:
  • 0 Henk 'm!

  • rogierslag
  • Registratie: Maart 2005
  • Laatst online: 14-10-2024
Is het performance technisch niet gewoon beter om hier een database voor te gebruiken?

Over het algemeen worden databases namelijk beoordeeld op hun snelheid en dergelijke. Ook kan je er van uitgaan dat de databaseprogrammeur meer verstand van allerlei optimalisaties heeft dan jij. Conclusie is dan dat de databaseoptie waarschijnlijk sneller zal zijn (nml: database is sneller dan webserver met inlezen en de database sorteerd sneller). Ook leest één SQL query makkelijker dan deze code.

Verder cachen de meeste databases ook nog eens duchtig (dat betekent veel), dus hoeft de webserver het bestand niet eerst in te lezen (harddisk activiteit) maar serveert de databaseserver alles zo uit zijn/haar cache. Tenslotte krijg je ook nog eens geen problemen met het locken van een bestand. Dit is nog eens afgezien dat je nou je php laat sorteren en dus eerst alle (inclusief irrelevante) tekst ophaalt en dan sorteert. Door de beperkte doorvoersnelheid van de HD is dit de bottleneck. Via SQL selecteer je alleen wat je wilt, en trek je een hoop minder over je bottleneck heen

Tip: Als er door een kundigere programmeur al een wiel is gebouwd, probeer dan zelf geen achthoek te schapen maar gebruik dat wiel! (dwz: SQL-databases zijn voor dit werk gemaakt, bestanden inlezen onder een webserver niet)

Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
Je filesystem en harde schijf hebben ook gewoon cache hoor rogier :) Voor een normaal klein textbestand maakt het echt niet zoveel uit, als't minder dan een halve meg is zul je er qua performance weinig aan merken.

Databases zijn fijn en snel, maar lang niet altijd beschikbaar en kosten je vaak meer programmeerwerk - mijn functie haalt gegevens op en filtert ze in effectief 6 regels code, dat lukt me niet met een databaseconnectie (er vanuit gaande dat ik niet m'n eigen DB class gebruik dan, dat is valsspelen :+)

Bitage: het punt is dat Mac's niet \n als line-ending hebben maar \r. Dat zijn typisch van die dingen waar je zelf doorgaans niet aan denkt en die PHP prima voor je kan doen door de juiste functie voor de juiste taak te gebruiken :) Over het al dan niet mooi zijn van ze doe ik maar geen uitspraken ;)

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • DeepFreeze.NL
  • Registratie: April 2006
  • Laatst online: 02-03 08:01
@ Bitage, om te zorgen dat alleen op de 1e letter van de regel gezocht wordt heb ik ook gebruik gemaakt van:

PHP:
1
$pos = strpos(substr($value, 0, 1), $letter);

Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
DeepFreeze.NL schreef op zaterdag 08 december 2007 @ 13:26:
@ Bitage, om te zorgen dat alleen op de 1e letter van de regel gezocht wordt heb ik ook gebruik gemaakt van:

PHP:
1
$pos = strpos(substr($value, 0, 1), $letter);
Wellicht een leuk weetje: PHP werkt in bepaalde aspecten net zoals de taal waar'ie van af stamt (C), dus je mag ook gewoon dit doen:
PHP:
1
$post = stripos($value[0], $letter);

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • DeepFreeze.NL
  • Registratie: April 2006
  • Laatst online: 02-03 08:01
*Knip* :X

[ Voor 97% gewijzigd door DeepFreeze.NL op 08-12-2007 14:03 ]


Acties:
  • 0 Henk 'm!

  • rogierslag
  • Registratie: Maart 2005
  • Laatst online: 14-10-2024
FragFrog schreef op zaterdag 08 december 2007 @ 13:11:
Je filesystem en harde schijf hebben ook gewoon cache hoor rogier :) Voor een normaal klein textbestand maakt het echt niet zoveel uit, als't minder dan een halve meg is zul je er qua performance weinig aan merken.
Als ik een modern breezah sletje volledig normaal alcohol consumerend tienermeisje was geweest had ik nou even "du-uh" gezegd ;)

Alleen cachen databases als MySQL veel effectiever (meen ik). Als je webserver veel I/O operaties heeft is dat bestandje zo weer uit je cache weggehuppeld, terug naar de diepe regionen van de platters.

MySQL gebruikt volgens mij (ik weet het niet uit mijn hoofd) gewoon zoveel mogelijk RAM als er niet nodig is door andere programma´s. In plaats van enkele Mb´s aan cache kan je dan letterlijk honderden megabytes in je interne geheugen laten wonen. T.net gebruikt die tactiek ook om dingen als zoeken sterk te versnellen dacht ik

Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
rogierslag schreef op maandag 10 december 2007 @ 03:22:
Alleen cachen databases als MySQL veel effectiever (meen ik). Als je webserver veel I/O operaties heeft is dat bestandje zo weer uit je cache weggehuppeld, terug naar de diepe regionen van de platters.
Niet per se waar. Als het bestandje vaak opgevraagd wordt zit het ook gewoon in de cache van je operating system's file manager, als het niet vaak opgevraagd wordt maakt het ook niet uit.
MySQL gebruikt volgens mij (ik weet het niet uit mijn hoofd) gewoon zoveel mogelijk RAM als er niet nodig is door andere programma´s. In plaats van enkele Mb´s aan cache kan je dan letterlijk honderden megabytes in je interne geheugen laten wonen. T.net gebruikt die tactiek ook om dingen als zoeken sterk te versnellen dacht ik
Pertinent niet waar. MySQL gebruikt zoveel geheugen als je hem toewijst, zou niet moeten zijn dat je SQL server zomaar al het RAM opvreet. Je -kan- 'm wel zo instellen, maar dat is zeker standaard echt niet zo.

Daarnaast vergeet je dat een MySQL connectie veel meer resources gebruikt dan een filesystem request. Voor 1 kleine array van gegevens die alleen maar gelezen wordt is het daarom onzin een MySQL server te gebruiken.

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • rogierslag
  • Registratie: Maart 2005
  • Laatst online: 14-10-2024
oke, mijn fout. Ik meende echt dat het gedrag zo ging, maar gelukkig ben ik niet onfeilbaar.

Wat volgens mij wel zo is: zodra je een actieve databaseconnectie hebt is het sneller om overige data ook uit diezelfde databaseconnectie te halen dan weer resourcen aan te spreken als bestanden. Hier twijfel ik nu echter ook over. Weet je hier meer van?

Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
Dat is inderdaad (bijna) altijd sneller, maar de winst die je haalt is verwaarloosbaar ten opzichte van de tijd die het duurt om de daadwerkelijke query uit te voeren :)

Ik zit momenteel midden in een project voor een enorme reissite die binnenkort gelanceerd wordt, daar hebben we ook een degelijke profiler achter gebouwd die precies bijhoudt hoe lang de server ergens mee bezig is (tot op individuele queries aan toe). Doorgaans zien we een redelijk random bereik aan executietijden per query (zeker als we in debug mode gaan komen we geregeld aan 300 ~ 400 queries) en gaat de echte snelheidswinst vooral zitten in het minimaliseren van het aantal queries. Vooral opmerkelijk voor mij was dat een query van 30, 40 regels met een zut subqueries ertussen vaak maar twee a drie keer zo lang duurt als een willekeurige losse kleine query.

Als je't mij vraagt is een relatief groot deel van query tijden dan ook verspild aan 'overhead', zaken als request naar de SQL server sturen, wachten op een response, files laden, een enkele keer indexen rebuilden, etc. Als dat allemaal eenmaal gedaan is zijn de daadwerkelijke bewerkingen eigenlijk zo klaar, maar dat betekent wel dat als je geen bewerkingen op je data hoeft te doen (of condities erop toepast bijvoorbeeld) het helemaal geen kwaad kan een textfile te gebruiken. Onze classtree bijvoorbeeld wordt automatisch als XML bestand opgeslagen, dat is grofweg 4x zo snel als de queries elke pageview opnieuw uitvoeren :)

[ Site ] [ twitch ] [ jijbuis ]

Pagina: 1