如何用php从文件中删除一行?

发布于 2024-11-02 02:30:04 字数 126 浏览 1 评论 0原文

我有一个名为 $dir 的文件和一个名为 $line 的字符串,我知道这个字符串是该文件的完整行,但我不知道它的行号,我想将其从文件中删除,我该怎么办?

可以用awk吗?

I have a file named $dir and a string named $line, I know that this string is a complete line of that file but I don't know its line number and I want to remove it from file, what should I do?

Is it possible to use awk?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

泪眸﹌ 2024-11-09 02:30:04
$contents = file_get_contents($dir);
$contents = str_replace($line, '', $contents);
file_put_contents($dir, $contents);
$contents = file_get_contents($dir);
$contents = str_replace($line, '', $contents);
file_put_contents($dir, $contents);
梦纸 2024-11-09 02:30:04

逐行读取,并将除匹配行之外的所有行写入另一个文件。然后替换原来的文件。

Read the lines one by one, and write all but the matching line to another file. Then replace the original file.

哽咽笑 2024-11-09 02:30:04

这只会查看每一行,如果不是您想要删除的内容,它将被推送到一个数组,该数组将被写回文件。

 $DELETE = "the_line_you_want_to_delete";

 $data = file("./foo.txt");

 $out = array();

 foreach($data as $line) {
     if(trim($line) != $DELETE) {
         $out[] = $line;
     }
 }

 $fp = fopen("./foo.txt", "w+");
 flock($fp, LOCK_EX);
 foreach($out as $line) {
     fwrite($fp, $line);
 }
 flock($fp, LOCK_UN);
 fclose($fp);  

this will just look over every line and if it not what you want to delete, it gets pushed to an array that will get written back to the file. see this

 $DELETE = "the_line_you_want_to_delete";

 $data = file("./foo.txt");

 $out = array();

 foreach($data as $line) {
     if(trim($line) != $DELETE) {
         $out[] = $line;
     }
 }

 $fp = fopen("./foo.txt", "w+");
 flock($fp, LOCK_EX);
 foreach($out as $line) {
     fwrite($fp, $line);
 }
 flock($fp, LOCK_UN);
 fclose($fp);  
捶死心动 2024-11-09 02:30:04

不使用awk也可以解决:

function remove_line($file, $remove) {
    $lines = file($file, FILE_IGNORE_NEW_LINES);
    foreach($lines as $key => $line) {
        if($line === $remove) unset($lines[$key]);
    }
    $data = implode(PHP_EOL, $lines);
    file_put_contents($file, $data);
}

It can be solved without the use of awk:

function remove_line($file, $remove) {
    $lines = file($file, FILE_IGNORE_NEW_LINES);
    foreach($lines as $key => $line) {
        if($line === $remove) unset($lines[$key]);
    }
    $data = implode(PHP_EOL, $lines);
    file_put_contents($file, $data);
}
御守 2024-11-09 02:30:04

另一种方法是逐行读取文件,直到找到匹配项,然后将文件截断到该点,然后附加其余行。

Another approach is to read the file line by line until you find a match, then truncate the file to that point, and then append the rest of the lines.

国际总奸 2024-11-09 02:30:04

这里的所有答案都有一个共同点,即它们将完整的文件加载到内存中。下面是删除一行(或多行)而不将文件内容复制到变量的实现。

这个想法是迭代文件行。如果应删除一行,则行长度将添加到 $byte_offset 中。然后下一行“向上”移动 $byte_offset 字节。这是通过以下所有行完成的。如果处理了所有行,则文件最后 $byte_offset 字节将被删除。

我想这对于较大的文件来说会更快,因为没有复制任何内容。我猜想在某些文件大小下,其他答案根本不起作用,而这个答案应该起作用。但我没有测试过。

用法:

$file = fopen("path/to/file", "a+");
// remove lines 1 and 2 and the line containing only "line"
fremove_line($file, 1, 2, "line");
fclose($file);

fremove_line()函数的代码:

/**
 * Remove the `$lines` by either their line number (as an int) or their content
 * (without trailing new-lines).
 * 
 * Example:
 * ```php
 * $file = fopen("path/to/file", "a+"); // must be opened writable
 * // remove lines 1 and 2 and the line containing only "line"
 * fremove_line($file, 1, 2, "line");
 * fclose($file);
 * ```
 * 
 * @param resource $file The file resource opened by `fopen()`
 * @param int|string ...$lines The one-based line number(s) or the full line 
 *     string(s) to remove, if the line does not exist, it is ignored
 * 
 * @return boolean True on success, false on failure
 */
function fremove_line($file, ..$lines): bool{
    // set the pointer to the start of the file
    if(!rewind($file)){
        return false;
    }

    // get the stat for the full size to truncate the file later on
    $stat = fstat($file);
    if(!$stat){
        return false;
    }

    $current_line = 1; // change to 0 for zero-based $lines
    $byte_offset = 0;
    while(($line = fgets($file)) !== false){
        // the bytes of the lines ("number of ASCII chars")
        $line_bytes = strlen($line);

        if($byte_offset > 0){
            // move lines upwards
            // go back the `$byte_offset`
            fseek($file, -1 * ($byte_offset + $line_bytes), SEEK_CUR);
            // move the line upwards, until the `$byte_offset` is reached
            if(!fwrite($file, $line)){
                return false;
            }
            // set the file pointer to the current line again, `fwrite()` added `$line_bytes`
            // already
            fseek($file, $byte_offset, SEEK_CUR);
        }

        // remove trailing line endings for comparing
        $line_content = preg_replace("~[\n\r]+$~", "", $line);

        if(in_array($current_line, $lines, true) || in_array($line_content, $lines, true)){
            // the `$current_line` should be removed so save to skip the number of bytes 
            $byte_offset += $line_bytes;
        }

        // keep track of the current line
        $current_line++;
    }

    // remove the end of the file
    return ftruncate($file, $stat["size"] - $byte_offset);
}

All answeres here have in common, that they load the complete file into the memory. Here is an implementation of removing one (or more) line(s) without coyping the files content into a variable.

The idea is to iterate over the files lines. If a line should be removed, the lines length is added to the $byte_offset. The next line is then moved $byte_offset bytes "upwards". This is done with all following lines. If all lines are processed, the files last $byte_offset bytes are removed.

I guess that this is faster for bigger files because nothing is copied. And I guess that at some file size the other answers do not work at all while this one should. But I didn't test it.

Usage:

$file = fopen("path/to/file", "a+");
// remove lines 1 and 2 and the line containing only "line"
fremove_line($file, 1, 2, "line");
fclose($file);

The code of the fremove_line() function:

/**
 * Remove the `$lines` by either their line number (as an int) or their content
 * (without trailing new-lines).
 * 
 * Example:
 * ```php
 * $file = fopen("path/to/file", "a+"); // must be opened writable
 * // remove lines 1 and 2 and the line containing only "line"
 * fremove_line($file, 1, 2, "line");
 * fclose($file);
 * ```
 * 
 * @param resource $file The file resource opened by `fopen()`
 * @param int|string ...$lines The one-based line number(s) or the full line 
 *     string(s) to remove, if the line does not exist, it is ignored
 * 
 * @return boolean True on success, false on failure
 */
function fremove_line($file, ..$lines): bool{
    // set the pointer to the start of the file
    if(!rewind($file)){
        return false;
    }

    // get the stat for the full size to truncate the file later on
    $stat = fstat($file);
    if(!$stat){
        return false;
    }

    $current_line = 1; // change to 0 for zero-based $lines
    $byte_offset = 0;
    while(($line = fgets($file)) !== false){
        // the bytes of the lines ("number of ASCII chars")
        $line_bytes = strlen($line);

        if($byte_offset > 0){
            // move lines upwards
            // go back the `$byte_offset`
            fseek($file, -1 * ($byte_offset + $line_bytes), SEEK_CUR);
            // move the line upwards, until the `$byte_offset` is reached
            if(!fwrite($file, $line)){
                return false;
            }
            // set the file pointer to the current line again, `fwrite()` added `$line_bytes`
            // already
            fseek($file, $byte_offset, SEEK_CUR);
        }

        // remove trailing line endings for comparing
        $line_content = preg_replace("~[\n\r]+$~", "", $line);

        if(in_array($current_line, $lines, true) || in_array($line_content, $lines, true)){
            // the `$current_line` should be removed so save to skip the number of bytes 
            $byte_offset += $line_bytes;
        }

        // keep track of the current line
        $current_line++;
    }

    // remove the end of the file
    return ftruncate($file, $stat["size"] - $byte_offset);
}
匿名的好友 2024-11-09 02:30:04

如果您要在一行中查找子字符串 (ID) 并希望用新行替换旧行,这也很有用。

代码:

$contents = file_get_contents($dir);
$new_contents = "";
if (strpos($contents, $id) !== false) { // if file contains ID
    $contents_array = explode(PHP_EOL, $contents);
    foreach ($contents_array as &$record) {    // for each line
        if (strpos($record, $id) !== false) { // if we have found the correct line
            continue; // we've found the line to delete - so don't add it to the new contents.
        } else {
            $new_contents .= $record . "\r"; // not the correct line, so we keep it
        }
    }
    file_put_contents($dir, $new_contents); // save the records to the file
    echo json_encode("Successfully updated record!");
}
else {
    echo json_encode("failed - user ID ". $id ." doesn't exist!");
}

示例:

输入:“123,student”

旧文件:

身份证、职业

123,学生

124,砖层

运行代码将文件更改为:

新文件:

身份证、职业

124,砖层

This is also good if you're looking for a substring (ID) in a line and want to replace the old line with the a new line.

Code:

$contents = file_get_contents($dir);
$new_contents = "";
if (strpos($contents, $id) !== false) { // if file contains ID
    $contents_array = explode(PHP_EOL, $contents);
    foreach ($contents_array as &$record) {    // for each line
        if (strpos($record, $id) !== false) { // if we have found the correct line
            continue; // we've found the line to delete - so don't add it to the new contents.
        } else {
            $new_contents .= $record . "\r"; // not the correct line, so we keep it
        }
    }
    file_put_contents($dir, $new_contents); // save the records to the file
    echo json_encode("Successfully updated record!");
}
else {
    echo json_encode("failed - user ID ". $id ." doesn't exist!");
}

Example:

input: "123,student"

Old file:

ID,occupation

123,student

124,brick layer

Running the code will change file to:

New file:

ID,occupation

124,brick layer

南渊 2024-11-09 02:30:04

将文本转换为数组,删除第一行并重新转换为文本

$line=explode("\r\n",$text);
unset($line[0]);
$text=implode("\r\n",$line);

Convert text to array, remove first line and reconvert to text

$line=explode("\r\n",$text);
unset($line[0]);
$text=implode("\r\n",$line);
她比我温柔 2024-11-09 02:30:04

我认为处理文件的最佳方法是将它们视为字符串:

/**
 * Removes the first found line inside the given file.
 *
 * @param string $line The line content to be searched.
 * @param string $filePath Path of the file to be edited.
 * @param bool $removeOnlyFirstMatch Whether to remove only the first match or
 * the whole matches.
 * @return bool If any matches found (and removed) or not.
 *
 * @throw \RuntimeException If the file is empty.
 * @throw \RuntimeException When the file cannot be updated.
 */
function removeLineFromFile(
    string $line,
    string $filePath,
    bool $removeOnlyFirstMatch = true
): bool {
    // You can wrap it inside a try-catch block
    $file = new \SplFileObject($filePath, "r");

    // Checks whether the file size is not zero
    $fileSize = $file->getSize();
    if ($fileSize !== 0) {
        // Read the whole file 
        $fileContent = $file->fread($fileSize);
    } else {
        // File is empty
        throw new \RuntimeException("File '$filePath' is empty");
    }

    // Free file resources
    $file = null;

    // Divide file content into its lines
    $fileLineByLine = explode(PHP_EOL, $fileContent);

    $found = false;
    foreach ($fileLineByLine as $lineNumber => $thisLine) {
        if ($thisLine === $line) {
            $found = true;
            unset($fileLineByLine[$lineNumber]);

            if ($removeOnlyFirstMatch) {
                break;
            }
        }
    }

    // We don't need to update file either if the line not found
    if (!$found) {
        return false;
    }

    // Join lines together
    $newFileContent = implode(PHP_EOL, $fileLineByLine);

    // Finally, update the file
    $file = new \SplFileObject($filePath, "w");
    if ($file->fwrite($newFileContent) !== strlen($newFileContent)) {
        throw new \RuntimeException("Could not update the file '$filePath'");
    }

    return true;
}

以下是正在执行的操作的简要说明:获取整个文件内容,将内容拆分为行(即作为数组),找到匹配项( es)并删除它们,将所有行连接在一起,并将结果保存回文件(仅当发生任何更改时)。

现在让我们使用它:

// $dir is your filename, as you mentioned
removeLineFromFile($line, $dir);

注意:

  • 您可以使用 fopen() 系列函数代替 SplFileObject,但我建议使用对象形式,因为它是基于异常的,更健壮且更高效(至少在本例中是这样)。

  • 使用 foreach 迭代时 unset() 数组的元素是安全的(有一条注释 here 显示它可能会导致意外结果,但这是完全错误的:正如您在示例代码中看到的,$value被复制(即它不是引用),删除数组元素不会影响它)。

  • $line 不应包含像 \n 这样的换行符,否则,您可能会执行大量冗余搜索。

  • 不要使用

    $fileLineByLine[$lineNumber] = "";
    // 或者甚至
    $fileLineByLine[$lineNumber] = null;
    

    而不是

    unset($fileLineByLine[$key]);
    

    原因是,第一种情况不会删除该行,它只是清除该行(并且会保留不需要的空行)。

希望有帮助。

I think the best way to work with files is to act them like strings:

/**
 * Removes the first found line inside the given file.
 *
 * @param string $line The line content to be searched.
 * @param string $filePath Path of the file to be edited.
 * @param bool $removeOnlyFirstMatch Whether to remove only the first match or
 * the whole matches.
 * @return bool If any matches found (and removed) or not.
 *
 * @throw \RuntimeException If the file is empty.
 * @throw \RuntimeException When the file cannot be updated.
 */
function removeLineFromFile(
    string $line,
    string $filePath,
    bool $removeOnlyFirstMatch = true
): bool {
    // You can wrap it inside a try-catch block
    $file = new \SplFileObject($filePath, "r");

    // Checks whether the file size is not zero
    $fileSize = $file->getSize();
    if ($fileSize !== 0) {
        // Read the whole file 
        $fileContent = $file->fread($fileSize);
    } else {
        // File is empty
        throw new \RuntimeException("File '$filePath' is empty");
    }

    // Free file resources
    $file = null;

    // Divide file content into its lines
    $fileLineByLine = explode(PHP_EOL, $fileContent);

    $found = false;
    foreach ($fileLineByLine as $lineNumber => $thisLine) {
        if ($thisLine === $line) {
            $found = true;
            unset($fileLineByLine[$lineNumber]);

            if ($removeOnlyFirstMatch) {
                break;
            }
        }
    }

    // We don't need to update file either if the line not found
    if (!$found) {
        return false;
    }

    // Join lines together
    $newFileContent = implode(PHP_EOL, $fileLineByLine);

    // Finally, update the file
    $file = new \SplFileObject($filePath, "w");
    if ($file->fwrite($newFileContent) !== strlen($newFileContent)) {
        throw new \RuntimeException("Could not update the file '$filePath'");
    }

    return true;
}

Here is a brief description of what is being done: Get the whole file content, split the content into its lines (i.e. as an array), find the match(es) and remove them, join all lines together, and save the result back to the file (only if any changes happened).

Let's now use it:

// $dir is your filename, as you mentioned
removeLineFromFile($line, $dir);

Notes:

  • You can use fopen() family functions instead of SplFileObject, but I do recommend the object form, as it's exception-based, more robust and more efficient (in this case at least).

  • It's safe to unset() an element of an array being iterated using foreach (There's a comment here showing it can lead unexpected results, but it's totally wrong: As you can see in the example code, $value is copied (i.e. it's not a reference), and removing an array element does not affect it).

  • $line should not have new line characters like \n, otherwise, you may perform lots of redundant searches.

  • Don't use

    $fileLineByLine[$lineNumber] = "";
    // Or even
    $fileLineByLine[$lineNumber] = null;
    

    instead of

    unset($fileLineByLine[$key]);
    

    The reason is, the first case doesn't remove the line, it just clears the line (and an unwanted empty line will remain).

Hope it helps.

筱武穆 2024-11-09 02:30:04

像这样:

file_put_contents($filename, str_replace($line . "\r\n", "", file_get_contents($filename)));

Like this:

file_put_contents($filename, str_replace($line . "\r\n", "", file_get_contents($filename)));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文