Bagaimana cara membaca file besar baris demi baris?

470

Saya ingin membaca file baris demi baris, tetapi tanpa memuatnya dalam memori.

File saya terlalu besar untuk dibuka di memori, dan jika coba lakukan saya selalu keluar dari kesalahan memori.

Ukuran file adalah 1 GB.

adnan masood
sumber
lihat jawaban saya di tautan
Sohail Ahmed
7
Anda harus menggunakan fgets()tanpa $lengthparameter.
Carlos
26
Apakah Anda ingin menandai sebagai jawaban pada salah satu dari yang berikut ini?
Kim Stacks

Jawaban:

685

Anda dapat menggunakan fgets()fungsi ini untuk membaca file baris demi baris:

$handle = fopen("inputfile.txt", "r");
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        // process the line read.
    }

    fclose($handle);
} else {
    // error opening the file.
} 
codaddict
sumber
3
Bagaimana akun ini untuk too large to open in memorybagian itu?
Starx
64
Anda tidak membaca seluruh file dalam memori. Memori maks yang diperlukan untuk menjalankan ini tergantung pada garis terpanjang dalam input.
codaddict
13
@Brandin - Moot - Dalam situasi itu, pertanyaan yang diajukan, yaitu membaca file LINE BY LINE, tidak memiliki hasil yang jelas.
ToolmakerSteve
3
@ToolmakerSteve Kemudian tentukan apa yang harus terjadi. Jika mau, Anda bisa langsung mencetak pesan "Line terlalu lama; menyerah." dan itu adalah hasil yang didefinisikan dengan baik juga.
Brandin
2
Bisakah sebuah baris mengandung boolean false? Jika demikian maka metode ini akan berhenti tanpa mencapai akhir file. Contoh # 1 di URL ini php.net/manual/en/function.fgets.php menyarankan bahwa kadang-kadang Fool dapat mengembalikan boolean false meskipun file belum tercapai. Di bagian komentar pada halaman itu orang melaporkan bahwa fgets () tidak selalu mengembalikan nilai yang benar, jadi lebih aman menggunakan feof sebagai persyaratan loop.
cjohansson
130
if ($file = fopen("file.txt", "r")) {
    while(!feof($file)) {
        $line = fgets($file);
        # do same stuff with the $line
    }
    fclose($file);
}
Syuaa SE
sumber
8
Seperti yang dikatakan @ Cuse70 dalam jawabannya, ini akan menyebabkan loop infinite jika file tidak ada atau tidak dapat dibuka. Tes if($file)sebelum loop sementara
FrancescoMM
10
Saya tahu ini sudah lama, tetapi: menggunakan while (! Feof ($ file)) tidak disarankan. Silahkan lihat di sini.
Kevin Van Ryckegem
BTW: "Jika tidak ada lagi data untuk dibaca di file pointer, maka FALSE dikembalikan." php.net/manual/en/function.fgets.php ... Untuk berjaga
orang biasa
2
feof()tidak ada lagi?
Ryan DuVal
94

Anda dapat menggunakan kelas antarmuka berorientasi objek untuk file - SplFileObject http://php.net/manual/en/splfileobject.fgets.php (PHP 5> = 5.1.0)

<?php

$file = new SplFileObject("file.txt");

// Loop until we reach the end of the file.
while (!$file->eof()) {
    // Echo one line from the file.
    echo $file->fgets();
}

// Unset the file to call __destruct(), closing the file handle.
$file = null;
elshnkhll
sumber
3
solusi yang jauh lebih bersih. terima kasih;) belum menggunakan kelas ini, ada fungsi yang lebih menarik di sini untuk dijelajahi: php.net/manual/en/class.splfileobject.php
Lukas Liesis
6
Terima kasih. Ya, misalnya Anda dapat menambahkan baris ini sebelumnya ketika $ file-> setFlags (SplFileObject :: DROP_NEW_LINE); untuk menghentikan baris baru di akhir baris.
elshnkhll
Sejauh yang saya bisa lihat tidak ada eof()fungsi di SplFileObject?
Chud37
3
Terima kasih! Juga, gunakan rtrim($file->fgets())untuk menelusur baris baru untuk setiap string baris yang dibaca jika Anda tidak menginginkannya.
racl101
@ Chud37 ya ada: php.net/manual/en/splfileobject.eof.php
Nathan F.
59

Jika Anda membuka file besar, Anda mungkin ingin menggunakan Generator di samping fgets () untuk menghindari memuat seluruh file ke dalam memori:

/**
 * @return Generator
 */
$fileData = function() {
    $file = fopen(__DIR__ . '/file.txt', 'r');

    if (!$file)
        die('file does not exist or cannot be opened');

    while (($line = fgets($file)) !== false) {
        yield $line;
    }

    fclose($file);
};

Gunakan seperti ini:

foreach ($fileData() as $line) {
    // $line contains current line
}

Dengan cara ini Anda dapat memproses masing-masing baris file di dalam foreach ().

Catatan: Generator membutuhkan> = PHP 5.5

Nino Škopac
sumber
3
Ini seharusnya menjadi jawaban yang diterima. Seratus kali lebih cepat dengan generator.
Tachi
1
Dan waaay lebih hemat memori.
Nino Škopac
2
@ NinoŠkopac: Bisakah Anda menjelaskan mengapa solusi ini lebih hemat memori? Misalnya, dibandingkan dengan SplFileObjectpendekatan.
k00ni
30

Gunakan teknik buffering untuk membaca file.

$filename = "test.txt";
$source_file = fopen( $filename, "r" ) or die("Couldn't open $filename");
while (!feof($source_file)) {
    $buffer = fread($source_file, 4096);  // use a buffer of 4KB
    $buffer = str_replace($old,$new,$buffer);
    ///
}
Starx
sumber
2
ini layak mendapatkan lebih banyak cinta, karena ini akan bekerja dengan file besar, bahkan file yang tidak memiliki carriage return atau garis yang sangat panjang ...
Jimmery
Saya tidak akan terkejut jika OP tidak benar-benar peduli dengan baris aktual dan hanya ingin mis. Melayani unduhan. Dalam hal ini, jawaban ini baik-baik saja (dan apa yang sebagian besar pembuat kode PHP akan lakukan).
Álvaro González
30

Ada file()fungsi yang mengembalikan array dari baris yang ada di file.

foreach(file('myfile.txt') as $line) {
   echo $line. "\n";
}
NoImaginationGuy
sumber
28
File satu GB akan dibaca menjadi memori dan dikonversi menjadi lebih dari satu array GB ... semoga berhasil.
FrancescoMM
4
Ini bukan jawaban untuk pertanyaan yang diajukan, tetapi itu menjawab pertanyaan yang lebih umum banyak orang ketika melihat di sini, jadi itu masih berguna, terima kasih.
pilavdzice
2
file () sangat nyaman untuk bekerja dengan file kecil. Terutama ketika Anda menginginkan sebuah array () sebagai hasil akhirnya.
functionvoid
ini adalah ide yang buruk dengan file yang lebih besar karena seluruh file sedang dibaca ke array sekaligus
Flash Thunder
Ini rusak parah pada file besar, sehingga metode yang tepat tidak bekerja.
ftrotter
19
foreach (new SplFileObject(__FILE__) as $line) {
    echo $line;
}
Pertanyaan Quolonel
sumber
Harus cinta oneliners
Nino Škopac
1
Pengusaha.
Pertanyaan Quolonel
1
Memori efisien dibandingkan dengan file().
Nobu
17

Jawaban yang jelas tidak ada di semua tanggapan.
PHP memiliki parser pembatas streaming yang rapi tersedia untuk tujuan itu.

$fp = fopen("/path/to/the/file", "r+");
while ($line = stream_get_line($fp, 1024 * 1024, "\n")) {
  echo $line;
}
fclose($fp);
John
sumber
Perlu dicatat bahwa kode ini hanya akan mengembalikan baris sampai baris kosong pertama muncul. Anda perlu menguji $ line! == false dalam kondisi sementarawhile (($line = stream_get_line($fp, 1024 * 1024, "\n")) !== false)
cebe
8

Hati-hati dengan hal-hal 'while (! Feof ... fgets ()', fboards bisa mendapatkan kesalahan (returnfing false) dan loop selamanya tanpa mencapai akhir file. loop berakhir, periksa feof; jika tidak benar, maka Anda memiliki kesalahan.

Cuse70
sumber
8

Ini cara saya mengelola dengan file yang sangat besar (diuji hingga 100G). Dan ini lebih cepat dari pada fgets ()

$block =1024*1024;//1MB or counld be any higher than HDD block_size*2
if ($fh = fopen("file.txt", "r")) { 
    $left='';
    while (!feof($fh)) {// read the file
       $temp = fread($fh, $block);  
       $fgetslines = explode("\n",$temp);
       $fgetslines[0]=$left.$fgetslines[0];
       if(!feof($fh) )$left = array_pop($lines);           
       foreach ($fgetslines as $k => $line) {
           //do smth with $line
        }
     }
}
fclose($fh);
Metodi Darzev
sumber
bagaimana Anda memastikan bahwa blok 1024 * 1024 tidak pecah di tengah garis?
user151496
1
@ user151496 mudah !! hitung ... 1.2.3.4
Omar El Don
@ OmarElDon ​​apa maksudmu?
Codex73
7

Salah satu solusi populer untuk pertanyaan ini akan memiliki masalah dengan karakter baris baru. Ini bisa diperbaiki dengan mudah dengan sederhana str_replace.

$handle = fopen("some_file.txt", "r");
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        $line = str_replace("\n", "", $line);
    }
    fclose($handle);
}
Tegan Snyder
sumber
6

SplFileObject berguna ketika berurusan dengan file besar.

function parse_file($filename)
{
    try {
        $file = new SplFileObject($filename);
    } catch (LogicException $exception) {
        die('SplFileObject : '.$exception->getMessage());
    }
    while ($file->valid()) {
        $line = $file->fgets();
        //do something with $line
    }

    //don't forget to free the file handle.
    $file = null;
}
xanadev
sumber
1
<?php
echo '<meta charset="utf-8">';

$k= 1;
$f= 1;
$fp = fopen("texttranslate.txt", "r");
while(!feof($fp)) {
    $contents = '';
    for($i=1;$i<=1500;$i++){
        echo $k.' -- '. fgets($fp) .'<br>';$k++;
        $contents .= fgets($fp);
    }
    echo '<hr>';
    file_put_contents('Split/new_file_'.$f.'.txt', $contents);$f++;
}
?>
Nguyễn Văn Cường
sumber
-8

Berfungsi untuk Membaca dengan pengembalian array

function read_file($filename = ''){
    $buffer = array();
    $source_file = fopen( $filename, "r" ) or die("Couldn't open $filename");
    while (!feof($source_file)) {
        $buffer[] = fread($source_file, 4096);  // use a buffer of 4KB
    }
    return $buffer;
}
sixvel.com
sumber
4
Ini akan membuat array tunggal lebih dari satu GB dalam memori (semoga sukses dengannya) dibagi bahkan tidak dalam garis tetapi dalam potongan karakter 4096 sewenang-wenang. Mengapa kamu mau melakukan itu?
FrancescoMM