Bu dosyadaki ilk satırı almak ve kaldırmak için en verimli yolu var mı?

9 Cevap php

I have a script which, each time is called, gets the first line of a file. Each line is known to be exactly of the same length (32 alphanumeric chars) and terminates with "\r\n". After getting the first line, the script removes it.

Bu, bu şekilde yapılır:

$contents = file_get_contents($file));
$first_line = substr($contents, 0, 32);
file_put_contents($file, substr($contents, 32 + 2)); //+2 because we remove also the \r\n

Açıkçası çalışır, ancak bunu yapmak için daha akıllı bir (veya daha fazla etkili) bir yolu olup olmadığını merak ediyorum?

Benim basit bir çözüm Ben temelde read and rewrite the entire file just to take and remove the first line.

9 Cevap

Dosyayı yeniden daha bu diğer yapmak için daha verimli bir yol yoktur.

Ben dün bu fikir geldi:

function read_and_delete_first_line($filename) {
  $file = file($filename);
  $output = $file[0];
  unset($file[0]);
  file_put_contents($filename, $file);
  return $output;
}

Ben genellikle bu tür bir şey için bir kabuk açılması tavsiye etmem, ama sen gerçekten büyük dosyalar üzerinde seyrek yapıyorsan, için söylenecek bir şey muhtemelen var:

$lines = `wc -l myfile` - 1;
`tail -n $lines myfile > newfile`;

Çok basit ve belleğe tüm dosya okuma anlamına gelmez.

Ama küçük dosyalar için bu tavsiye, veya çok sık kullanılması olmaz. Havai çok yüksek.

Sen dosyasının içine konumsal bilgi depolamak olabilir. Örneğin, dosyasının ilk 8 bayt bir tamsayıyı depolayabilir. Bu tamsayı dosyasındaki ilk gerçek satırın ofset bayt olduğunu.

Yani, artık satırları silmek asla. Bunun yerine, bir satır silme başlangıç ​​konumunu değiştirerek demektir. sonra ona fseek () ve normal olarak satırları okuyun.

Dosya sonunda büyük büyüyecektir. Periyodik dosya boyutunu azaltmak için yetim satırları temizlemek olabilir.

Ama cidden, sadece bir veritabanı kullanmak ve bu gibi şeyler yapmayın.

İşte tek yolu:

$contents = file($file, FILE_IGNORE_NEW_LINES);
$first_line = array_shift($contents);
file_put_contents($file, implode("\r\n", $contents));

Orada da bunu yapmak için sayısız yol var, ama tüm yöntemler bir şekilde ilk satırı ayıran ve geri kalanı tasarruf içerecektir. Sen tüm dosyayı yeniden kaçınamaz. Alternatif bir alır:

list($first_line, $contents) = explode("\r\n", file_get_contents($file), 2);
file_put_contents($file, implode("\r\n", $contents));

yerine bellekte hepsini koyarak, dosyayı yineleme

$handle = fopen("file", "r");
$first = fgets($handle,2048); #get first line.
$outfile="temp";
$o = fopen($outfile,"w");
while (!feof($handle)) {
    $buffer = fgets($handle,2048);
    fwrite($o,$buffer);
}
fclose($handle);
fclose($o);
rename($outfile,$file);

Ikinci bir geçici dosya oluşturmak, ne de bellekteki tüm dosyayı koymak gerek yok:

if ($handle = fopen("file", "c+")) {             // open the file in reading and editing mode
    if (flock($handle, LOCK_EX)) {               // lock the file, so no one can read or edit this file 
        while (($line = fgets($handle, 4096)) !== FALSE) { 
            if (isset($write_position)) {        // move the line to previous position, except the first line
                $read_position = ftell($handle); // get actual line
                fseek($handle, $write_position); // move to previous position
                fputs($handle, $line);           // put actual line in previous position
                fseek($handle, $read_position);  // return to actual position
            }
            $write_position = ftell($handle);    // get actual position, so it will be the previous position in the next loop
        }
        fflush($handle);                         // write any pending change to file
        ftruncate($handle, $write_position);     // drop the repeated last line
        flock($handle, LOCK_UN);                 // unlock the file
    }
    fclose($handle);
}

Siz dosyayı () yöntemini kullanabilirsiniz.

/> Ilk satırı

$content = file('myfile.txt');
echo $content[0];  

Bu size 'dosya' fonksiyonunu kullanarak yapmak gibi bellekteki tüm dosya yüklemek için ihtiyacım yok, bir dosyanın ilk satırını kayacak. Belki küçük dosyalar için 'dosyası' ile biraz daha yavaş bir daha (belki ama değil bahis) fakat sorunsuz büyük dosyaları yönetmek mümkün değildir.

$firstline = false;
if($handle = fopen($logFile,'c+')){
    if(!flock($handle,LOCK_EX)){fclose($handle);continue;}
    $offset = 0;
    $len = filesize($logFile);
    while(($line = fgets($handle,4096)) !== false){
        if(!$firstline){$firstline = $line;$offset = strlen($firstline);continue;}
        $pos = ftell($handle);
        fseek($handle,$pos-strlen($line)-$offset);
        fputs($handle,$line);
        fseek($handle,$pos);
    }
    fflush($handle);
    ftruncate($handle,($len-$offset));
    flock($handle,LOCK_UN);
    fclose($handle);
}