Büyük bir dosya satır satır nasıl okunur?


472

Bir dosyayı satır satır okumak istiyorum, ancak tamamen belleğe yüklemeden.

Dosyam bellekte açılamayacak kadar büyük ve bunu yapmaya çalışırsanız her zaman bellek hatalarından kurtulurum.

Dosya boyutu 1 GB.



7
Parametre fgets()olmadan kullanmalısınız $length.
Carlos

26
Aşağıdakilerden herhangi birinde yanıt olarak işaretlemek ister misiniz?
Kim Stacks

Yanıtlar:


686

fgets()Dosyayı satır satır okumak için işlevi kullanabilirsiniz :

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

    fclose($handle);
} else {
    // error opening the file.
} 

3
Bu too large to open in memorykısım nasıl hesaplanıyor ?
Starx

64
Bellekteki tüm dosyayı okumuyorsunuz. Bunu çalıştırmak için gereken maksimum bellek, girişteki en uzun satıra bağlıdır.
codaddict

13
@Brandin - Moot - Bu durumlarda, LINE BY LINE dosyasını okumak için sorulan sorunun iyi tanımlanmış bir sonucu yoktur.
ToolmakerSteve

3
@ToolmakerSteve Sonra ne olması gerektiğini tanımlayın. İsterseniz "Satır çok uzun; pes etme" mesajını yazdırabilirsiniz. ve bu da iyi tanımlanmış bir sonuçtur.
Brandin

2
Bir satır bir mantıksal yanlış içerebilir mi? Eğer öyleyse, bu yöntem dosyanın sonuna ulaşmadan duracaktır. Php.net/manual/tr/function.fgets.php URL'sindeki Örnek 1, dosya sonuna henüz ulaşılmamış olsa da parolaların bazen boolean false döndürebileceğini göstermektedir. Bu sayfadaki yorum bölümünde, insanlar fgets () öğesinin her zaman doğru değerleri döndürmediğini bildirir, bu nedenle döngü koşullu olarak feof kullanmak daha güvenlidir.
cjohansson

131
if ($file = fopen("file.txt", "r")) {
    while(!feof($file)) {
        $line = fgets($file);
        # do same stuff with the $line
    }
    fclose($file);
}

8
@ Cuse70'in cevabında söylediği gibi, dosya yoksa veya açılamıyorsa, bu sonsuz bir döngüye yol açacaktır. if($file)Süre döngüsünden önce test edin
FrancescoMM

10
Bu eski olduğunu biliyorum, ama: using (! Feof ($ file)) önerilmez. Buraya bir bak.
Kevin Van Ryckegem

BTW: "Dosya tanıtıcısında okunacak başka veri yoksa, YANLIŞ döndürülür." php.net/manual/en/function.fgets.php ... Ne olur ne olmaz
sokaktaki adam

2
feof()artık yok mu
Ryan DuVal

94

Bir dosya için nesne yönelimli bir arayüz sınıfı kullanabilirsiniz - 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;

3
çok daha temiz bir çözüm. teşekkürler;) bu sınıfı henüz kullanmadım, keşfetmek için burada daha ilginç fonksiyonlar var: php.net/manual/tr/class.splfileobject.php
Lukas Liesis

6
Teşekkürler. Evet, örneğin bu satırı daha önce $ file-> setFlags (SplFileObject :: DROP_NEW_LINE); satır sonunda satır sonları bırakmak için.
elshnkhll

Görebildiğim kadarıyla eof()SplFileObject hiçbir işlevi yoktur ?
Chud37

3
Teşekkürler! Ayrıca, rtrim($file->fgets())istemiyorsanız okunan her satır dizesi için sondaki satır satırlarını şeritlemek için kullanın .
racl101


59

Büyük bir dosya açıyorsanız, tüm dosyayı belleğe yüklemekten kaçınmak için muhtemelen fgets () ile birlikte Jeneratörler kullanmak istersiniz:

/**
 * @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);
};

Şöyle kullanın:

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

Bu şekilde, foreach () içindeki tek tek dosya satırlarını işleyebilirsiniz.

Not: Jeneratörler> = PHP 5.5 gerektirir


3
Bunun yerine kabul edilmiş bir cevap olmalıdır. Jeneratörlerle yüz kat daha hızlı.
Tachi

1
Ve waaay daha bellek tasarruflu.
Nino Škopac

2
@ NinoŠkopac: Bu çözümün neden bellek açısından daha verimli olduğunu açıklayabilir misiniz? Örneğin, SplFileObjectyaklaşıma kıyasla .
k00ni

30

Dosyayı okumak için arabelleğe alma tekniklerini kullanın.

$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);
    ///
}

2
Bu, büyük dosyalar, hatta satırbaşı olmayan dosyalar veya aşırı uzun çizgilerle çalışacağı için daha fazla sevgiyi hak ediyor ...
Jimmery

OP gerçekten gerçek hatları umursamadı ve sadece bir indirme hizmet etmek istedim şaşırdım. Bu durumda, bu cevap gayet iyi (ve çoğu PHP kodlayıcısının zaten ne yapacağını).
Álvaro González

30

file()Dosyada bulunan satır dizisini döndüren bir işlev vardır.

foreach(file('myfile.txt') as $line) {
   echo $line. "\n";
}

28
Bir GB dosyasının tümü belleğe okunacak ve birden fazla GB dizisine dönüştürülecektir ... iyi şanslar.
FrancescoMM

4
Bu, sorulan sorunun cevabı değildi, ancak birçok insanın buraya bakarken sahip olduğu daha yaygın soruyu cevaplıyor, bu yüzden hala faydalıydı, teşekkürler.
pilavdzice

2
file () küçük dosyalarla çalışmak için çok uygundur. Özellikle de sonuç olarak bir dizi () istediğinizde.
functionvoid

tüm dosya aynı anda bir diziye okunuyor çünkü bu büyük dosyalar ile kötü bir fikirdir
Flash Thunder

Bu, büyük dosyalar üzerinde kötü bir şekilde bozulur, bu yüzden tam olarak çalışmayan yöntemdir.
ftrotter


17

Açık cevap tüm yanıtlarda yoktu.
PHP tam olarak bu amaçla yapılmış temiz bir akış sınırlayıcı ayrıştırıcı vardır.

$fp = fopen("/path/to/the/file", "r+");
while ($line = stream_get_line($fp, 1024 * 1024, "\n")) {
  echo $line;
}
fclose($fp);

Bu kodun ilk boş satır oluşana kadar yalnızca satır döndüreceği unutulmamalıdır. $ Line! == while while koşulunda test etmeniz gerekiyorwhile (($line = stream_get_line($fp, 1024 * 1024, "\n")) !== false)
cebe

8

'While (! Feof ... fgets ()' öğelerine dikkat edin, fgets bir hata alabilir (yanlış dönen) ve dosya sonuna ulaşmadan sonsuza kadar döngü yapabilir. döngü sona erer, kontrol edin; doğru değilse, bir hatayla karşılaştınız.


8

Bu şekilde çok büyük bir dosya (100G'ye kadar test edilmiş) ile yönetiyorum. Ve daha hızlı ()

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

1024 * 1024 bloğunun çizginin ortasında kırılmamasını nasıl sağlıyorsunuz?
user151496

1
@ user151496 kolay !! saymak ... 1.2.3.4
Omar El Don

@OmarElDon ​​ne demek istiyorsun?
Codex73

7

Bu sorunun popüler çözümlerinden biri yeni çizgi karakteriyle ilgili sorunlara sahip olacak. Bir basit ile oldukça kolay sabitlenebilir str_replace.

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

6

SplFileObject büyük dosyalarla uğraşırken kullanışlıdır.

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;
}

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++;
}
?>

-8

Dizi dönüşüyle ​​okuma işlevi

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;
}

4
Bu, bellekte birden fazla GB'den oluşan tek bir dizi (onunla iyi şanslar) satırlara değil, rastgele 4096 karakter parçalarına bölünür. Bunu neden dünyada yapmak istesin?
FrancescoMM
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.