Pertanyaan Secara efisien menghitung jumlah baris file teks. (200mb +)


Saya baru saja mengetahui bahwa skrip saya memberi saya kesalahan fatal:

Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 440 bytes) in C:\process_txt.php on line 109

Kalimat itu adalah ini:

$lines = count(file($path)) - 1;

Jadi saya pikir itu mengalami kesulitan memuat file ke memeory dan menghitung jumlah baris, apakah ada cara yang lebih efisien yang dapat saya lakukan tanpa masalah memori?

File teks yang saya perlukan untuk menghitung jumlah baris untuk rentang dari 2MB hingga 500MB. Mungkin sebuah Gig kadang-kadang.

Terima kasih atas bantuannya.


74
2018-01-29 14:26


asal


Jawaban:


Ini akan menggunakan lebih sedikit memori, karena tidak memuat seluruh file ke dalam memori:

$file="largefile.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
  $line = fgets($handle);
  $linecount++;
}

fclose($handle);

echo $linecount;

fgets memuat satu baris ke dalam memori (jika argumen kedua $length dihilangkan akan terus membaca dari aliran sampai mencapai akhir baris, yang adalah apa yang kita inginkan). Ini masih mungkin tidak secepat menggunakan sesuatu selain PHP, jika Anda peduli dengan waktu dinding serta penggunaan memori.

Satu-satunya bahaya dengan ini adalah jika ada garis yang sangat panjang (bagaimana jika Anda menemukan file 2GB tanpa jeda baris?). Dalam hal ini, Anda lebih baik melakukan slurping in in chunks, dan menghitung karakter end-of-line:

$file="largefile.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
  $line = fgets($handle, 4096);
  $linecount = $linecount + substr_count($line, PHP_EOL);
}

fclose($handle);

echo $linecount;

140
2018-01-29 14:31



Menggunakan loop of fgets() panggilan adalah solusi yang baik dan yang paling mudah untuk ditulis, namun:

  1. Meskipun secara internal file dibaca menggunakan buffer 8192 byte, kode Anda masih harus memanggil fungsi itu untuk setiap baris.

  2. secara teknis mungkin bahwa satu baris mungkin lebih besar dari memori yang tersedia jika Anda membaca file biner.

Kode ini membaca file dalam potongan 8kB masing-masing dan kemudian menghitung jumlah baris baru dalam potongan itu.

function getLines($file)
{
    $f = fopen($file, 'rb');
    $lines = 0;

    while (!feof($f)) {
        $lines += substr_count(fread($f, 8192), "\n");
    }

    fclose($f);

    return $lines;
}

Jika panjang rata-rata setiap baris paling banyak 4kB, Anda sudah mulai menabung pada fungsi panggilan, dan itu dapat bertambah ketika Anda memproses file besar.

Benchmark

Saya menjalankan tes dengan file 1GB; berikut hasilnya:

             +-------------+------------------+---------+
             | This answer | Dominic's answer | wc -l   |
+------------+-------------+------------------+---------+
| Lines      | 3550388     | 3550389          | 3550388 |
+------------+-------------+------------------+---------+
| Runtime    | 1.055       | 4.297            | 0.587   |
+------------+-------------+------------------+---------+

Waktu diukur dalam detik waktu nyata, lihat sini apa arti sebenarnya


97
2017-12-12 07:08



Solusi Object Berorientasi Sederhana

$file = new \SplFileObject('file.extension');

while($file->valid()) $file->fgets();

var_dump($file->key());

Memperbarui

Cara lain untuk melakukan ini adalah dengan PHP_INT_MAX di SplFileObject::seek metode.

$file = new \SplFileObject('file.extension', 'r');
$file->seek(PHP_INT_MAX);

echo $file->key() + 1; 

35
2017-07-24 13:18



Jika Anda menjalankan ini di host Linux / Unix, solusi termudah akan digunakan exec() atau serupa dengan menjalankan perintah wc -l $path. Pastikan saja Anda telah membersihkannya $path pertama untuk memastikan bahwa itu bukan sesuatu seperti "/ path / to / file; rm -rf /".


33
2018-01-29 14:30



Ada cara yang lebih cepat yang saya temukan yang tidak memerlukan perulangan melalui seluruh file

hanya pada sistem * nix, mungkin ada cara serupa di windows ...

$file = '/path/to/your.file';

//Get number of lines
$totalLines = intval(exec("wc -l '$file'"));

25
2018-03-17 21:18



Jika Anda menggunakan PHP 5.5, Anda dapat menggunakan generator. Ini akan TIDAK bekerja dalam versi PHP sebelum 5.5 sekalipun. Dari php.net:

"Generator menyediakan cara mudah untuk mengimplementasikan iterator sederhana tanpa overhead atau kompleksitas penerapan kelas yang mengimplementasikan antarmuka Iterator."

// This function implements a generator to load individual lines of a large file
function getLines($file) {
    $f = fopen($file, 'r');

    // read each line of the file without loading the whole file to memory
    while ($line = fgets($f)) {
        yield $line;
    }
}

// Since generators implement simple iterators, I can quickly count the number
// of lines using the iterator_count() function.
$file = '/path/to/file.txt';
$lineCount = iterator_count(getLines($file)); // the number of lines in the file

8
2017-10-12 01:53



Ini adalah tambahan untuk Wallace de Souza larutan

Ini juga melompati garis-garis kosong sambil menghitung:

function getLines($file)
{
    $file = new \SplFileObject($file, 'r');
    $file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | 
SplFileObject::DROP_NEW_LINE);
    $file->seek(PHP_INT_MAX);

    return $file->key() + 1; 
}

4
2018-06-28 07:09