搜索

首页  >  问答  >  正文

反转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粉512729862476 天前654

全部回复(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
  • 取消回复