从大文件中删除一行的php方法

6

我想删除一个大文件(>20MB)中间的一行。我知道要删除的行在文件中的位置。

这是我目前的代码:

/**
 * Removes a line at a position from the file
 * @param  [int] $position  The position at the start of the line to be removed
 */
public function removeLineAt($position)
{
    $fp = fopen($this->filepath, "rw+");
    fseek($fp, $position);

    $nextLinePosition = $this->getNextLine($position, $fp);
    $lengthRemoved = $position - $nextLinePosition;
    $fpTemp = fopen('php://temp', "rw+");

    // Copy the bottom half (starting at line below the line to be removed)
    stream_copy_to_stream($fp, $fpTemp, -1, $nextLinePosition);

    // Seek to the start of the line to be removed
    fseek($fp, $position);
    rewind($fpTemp);

    // Copy the bottom half over the line to be removed
    stream_copy_to_stream($fpTemp, $fp);        

    fclose($fpTemp);
    fclose($fp);
}

然而,虽然上面的代码确实从文件中删除了该行;由于临时文件比原始文件短。原始文件的末尾仍然存在并且被复制。
例如: 原始文件是
1. a 2. b 3. c 4. d 5. e 删除该行后的文件可能如下所示
1. a 2. b 3. d 4. e 5. e 我想过通过$lengthRemoved量来修剪主文件的末尾,但我也想不到一个简单的方法。
有什么建议吗?
注意:该文件具有> 200,000行,有时> 300,000。我觉得将整个文件加载到数组(内存)中效率会相当低下。这就是为什么我尝试上述方法却遇到了一个问题。
以下是我根据您的帮助编写的最终函数!修改它以适应您的需求。
/**
 * Removes a line at a position from the file
 * @param  [int] $position  The position at the start of the line to be removed
 */
public function removeLineAt($position)
{
    $fp = fopen($this->filepath, "rw+");
    fseek($fp, $position);

    $nextLinePosition = $this->getNextLine($position, $fp);
    $lengthRemoved = $position - $nextLinePosition;
    $fpTemp = fopen('php://temp', "rw+");

    // Copy the bottom half (starting at line below the line to be removed)
    stream_copy_to_stream($fp, $fpTemp, -1, $nextLinePosition);

    // Remove the difference
    $newFileSize = ($this->totalBytesInFile($fp) + $lengthRemoved);
    ftruncate($fp, $newFileSize);

    // Seek to the start of the line to be removed
    fseek($fp, $position);
    rewind($fpTemp);

    // Copy the bottom half over the line to be removed
    stream_copy_to_stream($fpTemp, $fp);        

    fclose($fpTemp);
    fclose($fp);
}

可能是重复的问题:如何使用PHP从文本文件中删除某些行? - cmorrissey
1
该文件有超过200,000行,可能包含超过400,000行。我觉得将整个文件加载到数组(内存)中会非常低效。这就是为什么我尝试了上述方法,但遇到了一个问题。 - user4775085
2个回答

2

由于您的文件非常大,如果您的php安装允许使用该功能,您可能希望通过exec使用sed命令。

exec("sed '3d' fileName.txt");

这里的3表示所需的行号。


1

我认为你离解决方案已经非常接近了。

我建议你坚持删除文件末尾的$lengthRemoved,并建议在fclose()之前使用ftruncate($handle,$size);,其中大小是要截断的大小(大小=原始文件大小-lengthRemoved)。

http://www.php.net/manual/en/function.ftruncate.php


谢谢!那正是我在寻找的函数。 - user4775085
ftruncate会将文件加载到内存中吗? - Paresh Radadiya
不,ftruncate() 只是将句柄截断/缩短到请求的大小。您可能需要查看 fopen+fgets 或 file_get_contents 以将内容读入内存。 - Jens A. Koch
这段代码中占用内存较多的部分是stream_copy_to_stream。但它可以被feof,fwrite,fread所替代。 - Jens A. Koch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接