2010-03-08 77 views
14

我有一个脚本,每次被调用时,都会获取文件的第一行。已知每行的长度完全相同(32个字母数字字符),并以“\ r \ n”结尾。 获取第一行后,脚本将其删除。这是获取和删除文件中第一行的最有效方法吗?

这是这样完成的:

$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 

显然它的工作原理,但我不知道是否有更聪明(或更有效)的方式来做到这一点?

在我简单的解决方案中,我基本上是读取并重写整个文件,只是取出并删除第一行

+0

你可以让这个更高效的内存(做一个循环,一次读取一行,给他们写了一个在时间除了第一个),但它看起来很复杂,并且容易出错。我会和你一样。没有解决从第一个字节开始按顺序存储文件这一事实。 – 2010-03-08 21:25:14

+1

如果你可以将文件存储为索引,并通过索引执行所有的R/W,也许这个操作会更快,因为你可以简单地从索引中删除该行,这样做比在完整的文件。 但是,如果文件很小,那么I/O的成本将低于维护索引的开销。 – anijhaw 2010-03-08 21:26:21

+3

对于我能想到的类似问题,唯一的高度优化解决方案将涉及文件系统驱动程序中的IOCTL,该文件系统驱动程序会从文件中剪切第一个逻辑块(取决于硬件和实现的大小),而不会触及其余部分。但这是解决不存在问题的学术练习,绝对不是你想要的。 :) – 2010-03-08 21:28:33

回答

14

除了重写文件之外,没有更有效的方法来做到这一点。

2

这里有一种方法:

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

还有无数其他的方式来做到这一点还可以,但所有的方法会涉及某种方式分隔第一线,节省了休息。你无法避免重写整个文件。另一种选择是:

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

你的第一个例子会产生冗余换行符。如果没有file()的'FILE_IGNORE_NEW_LINES'标志,你不需要再用'implode()'来换行。 – 2010-03-08 21:11:54

+0

@fireeyedboy,不错,固定。 – 2010-03-08 22:34:15

+0

Ulman:+1非常有趣的代码,谢谢!我以前从未使用过文件功能。 – 2010-03-09 18:31:15

-1

您可以使用file()方法。

获取第一线

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

这对我很好。我不知道为什么有人让你失望。谢谢。我试图给你投票,但你仍然在0. :) – bozdoz 2011-05-28 21:58:08

+1

这种方法对大文件的效率非常低。 – 2013-09-16 06:16:22

+4

这不会删除第一行,它只是获取它。 – 2014-01-24 15:04:00

2

你可以存储位置信息到文件本身。例如,文件的前8个字节可以存储一个整数。该整数是文件中第一个实际行的字节偏移量。

因此,你永远不会删除行。相反,删除一条线意味着改变开始位置。 fseek(),然后像正常一样读取行。

该文件最终会变大。您可以定期清理孤行,以减小文件大小。

但严重的是,只是使用数据库,不要做这样的事情。

3

我通常不会建议开放对这种事情的壳,但如果你在真正的大文件很少这样做,有可能什么可说的:

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

很简单,它不涉及将整个文件读入内存。

我不会推荐这个小文件,或频繁使用,虽然。开销太高。

+2

反正这个效率并不高,而且代码也不可移植 – anijhaw 2010-03-08 21:23:50

+5

对于比如说3GB的文件,这将比这里发布的大多数答案更有效率。大部分发布的答案都会在大文件中出现内存不足错误。你说得对,它不是可移植的。有一个非常具体的情况下,这种解决方案将是有用/可接受的。 – 2010-03-08 23:12:21

+0

很高兴得到这个评论,即时通讯熟练的Linux和仍然有这个问题。 ty – iGNEOS 2016-09-14 17:45:27

4

可以迭代的,而不是把他们都在内存中的文件,

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

+1:我认为这样更有效率,但不是更快。如果文件太大而不能适应内存,证明它不会炸毁。 – 2011-05-18 14:45:32

17

我有这样的想法昨天来了:

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

您仍然阅读并重写entrie文件,但我承认这有点更好。 +1 – 2012-01-31 17:36:57

+0

谢谢,我正在寻找的是 – snapplex 2013-12-28 17:25:30

+1

不适用于大文件 – ana 2014-03-13 06:01:26

11

无需创建第二个临时文件,也没有放整个文件在内存中:

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 
       $write_position = 0; 
      } else { 
       $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 += strlen($line); // set write position to 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); 
} 
+1

您能否在代码旁边添加一些简短的注释以解释您在做什么? – 2014-01-24 15:08:28

+0

有趣的想法+1 – 2014-04-25 14:03:12

+0

这段代码不起作用,它只是简单地覆盖了一些行。在[Marcos Fernandez Ramo的回答]中查看基于这个工作版本(https://stackoverflow.com/questions/2404707/is-this-the-most-efficent-way-to-get-and-remove-first -line-in-file/23269245#23269245) – user 2014-12-01 06:07:15

5

这将移动文件的第一行,你不需要加载像你一样在内存中的整个文件使用'文件'功能。也许小文件比'文件'慢一点(可能但我打赌不是),但能够管理最大的文件没有问题。

$firstline = false; 
if($handle = fopen($logFile,'c+')){ 
    if(!flock($handle,LOCK_EX)){fclose($handle);} 
    $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); 
} 
+0

它比[Edakos]的答案好点(https://stackoverflow.com/questions/2404707/is-this-the-most-efficent-way-to-get-and-remove-first-line-in -file/20908373#20908373)? – user 2014-11-29 19:15:08

+0

我做到了,因为我无法让Edakos解决方案正常工作。在第3行中有一个错误的“继续”。 – 2014-11-30 19:30:11

+0

的确,该答案中的代码无效。 +1给你。 – user 2014-12-01 06:09:36

0

我觉得这是最好的任何文件的大小

$myfile = fopen("yourfile.txt", "r") or die("Unable to open file!"); 
$ch=1; 

while(!feof($myfile)) { 
    $dataline= fgets($myfile) . "<br>"; 
    if($ch == 2){ 
    echo str_replace(' ', '&nbsp;', $dataline)."\n"; 
    } 
    $ch = 2; 
} 
fclose($myfile); 
相关问题