skip to Main Content

I have this php script that reads the csv file, I would like it to start reading from the last line, basically the other way around.

<?php
$f = fopen("./data/data-esp8266-$idluf-$currentdata.csv", "r");
        fgets($f);
while (($line = fgetcsv($f)) !== false) {
       
        $row = $line[0];  // Dobbiamo ottenere la riga effettiva (è il primo elemento in un array di 1 elemento)
        $cells = explode(";",$row);
        echo "<tr>n";
        foreach ($cells as $cell) {
             echo "<td><a style='text-decoration:none;color:#fff;' class='tooltip' data-tool=' $cell'>" . htmlspecialchars($cell) . "</a></td>n";
        }
        echo "</tr>n";
}
fclose($f);
?>

4

Answers


  1. I think that you just need to read the file backwards line by line
    As shown here
    Read a file backwards line by line using fseek

    If you’re willing to load the whole file into memory you can use file_get_contents(<path>); , split the file by line end character and read the resulting array from len back down to 1

    Login or Signup to reply.
  2. If file is relatively small, you can read whole file into memory using file, than reverse it and loop:

    $csvLines = file("./data/data-esp8266-{$idluf}-{$currentdata}.csv",  FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    
    $csvLines = array_reverse($csvLines);
    
    foreach ($csvLines as $csvLine) {
        $line = str_getcsv($csvLine);
    
        /* ... */
    }
    
    Login or Signup to reply.
  3. Or you can use loop with for:

    <?php
    $lines = file("./data/data-esp8266-$idluf-$currentdata.csv", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    
    // Loop through the lines array in reverse
    for ($i = count($lines)-1; $i >= 0; $i--) {
        $row = $lines[$i];
        $cells = explode(";", $row);
        echo "<tr>n";
        foreach ($cells as $cell) {
            echo "<td><a style='text-decoration:none;color:#fff;' class='tooltip' data-tool=' $cell'>" . htmlspecialchars($cell) . "</a></td>n";
    
        }
        echo "</tr>n";
    }
    
    ?>
    

    This method should be faster than the other way based on the function array_reverse()

    Login or Signup to reply.
  4. If the file you’re reading were a "simple" file with nothing but line breaks as delimiters, then reading it backwards would be fairly simple. However, CSV is a more complex format with field and row delimiters, enclosures [quotes], and escapes. You could encounter data such as:

    1,foo,"hello world"
    2,bar,"hello 
    "world""
    3, baz,"hello again"
    

    Which is perfectly valid CSV, but would break most solutions currently proposed in this thread to read the data backwards.

    The most reliable way to do this would be to read the file forwards first, and then use that information to read it in reverse. The simple version is to just stuff everything into an array and then read that backwards, eg:

    $f = fopen("./data/data-esp8266-$idluf-$currentdata.csv", "r");
    fgets($f);
    
    $lines = [];
    while (($lines[] = fgetcsv($f)) !== false) {}
    
    for( $i=count($lines)-1; $i>=0; --$i ) {
        $line = lines[$i];
        $row = $line[0];  // Dobbiamo ottenere la riga effettiva (è il primo elemento in un array di 1 elemento)
        $cells = explode(";",$row);
        echo "<tr>n";
        foreach ($cells as $cell) {
            echo "<td><a style='text-decoration:none;color:#fff;' class='tooltip' data-tool=' $cell'>" . htmlspecialchars($cell) . "</a></td>n";
        }
        echo "</tr>n";
    }
    fclose($f);
    

    but if you’re processing a large file you may run into memory constraints trying to store all that data.

    An alternative would be to read the file forward once first, but only store the offsets in the file for the beginning of the records, and then use those offsets to iterate again in reverse.

    function csv_reverse($handle) {
        $offsets = [];
        do {
            $offsets[] = ftell($handle);
        } while($row = fgetcsv($handle));
        array_pop($offsets); // last offset is EOF
        
        for( $i=count($offsets)-1; $i>=0; --$i ) {
            fseek($handle, $offsets[$i]);
            yield fgetcsv($handle);
        }
    }
    
    $f = fopen("./data/data-esp8266-$idluf-$currentdata.csv", "r");
    fgets($f); // assuming that this discards the header row
    
    $lines = [];
    while (($lines[] = fgetcsv($f)) !== false) {}
    
    foreach( csv_reverse($f) as $line ) {
        // same as above
    }
    fclose($f);
    

    Live example: https://3v4l.org/McqpG

    There is a tradeoff here, in that the file must be traversed twice, but that’s going to have to be a necessary evil if there is a memory constraint.

    All this said, the better option would be to have this data in a database, if possible, which can trivially re-order the data for you on the fly. This code is already kinda-sorta reimplementing DB-related functionality, but worse.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search