首頁  >  問答  >  主體

反轉PHP讀取CSV文件

<p>我有一個PHP腳本,它可以讀取CSV文件,我希望它從最後一行開始讀取,也就是說,逆序讀取。 </p> <pre class="brush:php;toolbar:false;"><?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); ?></pre> <p><br /></p>
P粉512729862P粉512729862459 天前624

全部回覆(2)我來回復

  • P粉118698740

    P粉1186987402023-08-09 12:56:44

    如果你要讀取的文件是「簡單」的文件,只是以換行符作為分隔符,那麼從後往前讀取就會相當簡單。然而,CSV是一種更複雜的格式,有字段和行的分隔符號、引號(引號)和轉義字元。你可能會遇到數據,例如

    id,name,value
    1,foo,"hello world"
    2,bar,"hello 
    world"
    3, baz,"""hello 
    world"""
    

    這是一個完全有效的CSV,但是大多數目前在論壇上提出的解決方案都會在反向讀取資料時出現問題。

    最可靠的方法是先從檔案開頭讀取數據,然後使用這些資訊來反向讀取數據。最簡單的版本是直接將所有內容放入陣列中,然後反向讀取該數組,例如:

    #
    $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);
    

    但是如果你正在處理一個大文件,你可能會遇到記憶體限制,因為要儲存所有資料。

    一個替代方法是先一次讀取文件,然後只儲存記錄的開始處的文件偏移量,然後使用這些偏移量再次逆向迭代。


    #
    function csv_reverse($handle, ...$csv_options) {
        $offsets = [];
        do {
            $offsets[] = ftell($handle);
        } while($row = fgetcsv($handle, ...$csv_options));
        array_pop($offsets); // last offset is EOF
        
        for( $i=count($offsets)-1; $i>=0; --$i ) {
            fseek($handle, $offsets[$i]);
            yield fgetcsv($handle, ...$csv_options);
        }
    }
    
    $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);
    

    有這樣一個權衡,那就是檔案必須被遍歷兩次,但是如果有記憶體限制,那就不得不這麼做。

    所有這些說法,更好的選擇是把資料放在資料庫中,如果可能的話,資料庫可以輕鬆地重新排序資料。這個程式碼已經某種程度上在重新實作資料庫相關功能,但更糟。

    #

    回覆
    0
  • P粉513316221

    P粉5133162212023-08-09 10:41:12

    或者,也可以使用for迴圈

    <?php
    $lines = file("./data/data-esp8266-$idluf-$currentdata.csv", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    
    // 将循环倒序遍历lines数组
    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";
    }
    
    ?>

    此方法應該比基於函數array_reverse()的其它方法更快

    回覆
    0
  • 取消回覆