C 程序删除文件中的行序列

问题描述 投票:0回答:1

我认为 fgets 会让我的程序比使用 fgetc 更快,但由于我对 fgets 实际工作原理了解甚少,我的最终代码有很多问题,例如在文件底部留下空行,如果有人能弄清楚如何解决这个问题我很感激

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>

int main() {
    FILE *file, *f;
    char line[100], l[100]; // buffers to move and copy lines in the file
    int count = 1, // this counts the line we're in right now 
        StartDelete = 6, //here the first line of the sequence that we're gonna delete
        EndDelete = 10, // the line where deleting end
        size = 10; // how many lines in the file

    file = fopen("data.txt", "w+"); // Open the file in read-write mode
    f = fopen("data.txt", "r+"); // open another handle for the file to skip the lines we wanna delete 

    if (file == NULL || f == NULL) {
        printf("Error opening file.\n");
        return 1;
    }

    //take user's input 
    for ( int i = 0; i < size; ++i ) {
        printf("name %d: ", i+1);
        assert( fgets( line, sizeof(line), stdin ) );
        fprintf( file, "%s%s", "name: ", line );
    }
    
    rewind(file); // going back to the start of the file 
    
    //
    while( count <= EndDelete ) {
        count++;
        fgets(l, sizeof(l), f);
    }
    count = 0;// reset the lines counter to zero this time because of the way fgets work
    // Read each line and overwrite the file with lines that are not marked for deletion
    while (fgets(line, sizeof(line), file)) { //moving to the lines in the file
        count++;//the line conter grows as we move forward in lines
        if (count > StartDelete) {
            fseek(file, -strlen(line), SEEK_CUR);//going back to the beginning of the line again because of the mechanism of fgets 
            fwrite(l, sizeof(char), strlen(l), file);//overwriting 
            if(fgets(l, sizeof(l), f));//moving to the next line we wanna overwrite with
            else break;
        }
    }

    // Truncate the file to remove any remaining content beyond the new end of file
    fseek(file, ftell(file), SEEK_SET);
    ftruncate(fileno(file), ftell(file));

    fclose(file);

    return 0;
}

我尝试从中删除两个和三个

one
two
three
four

我最终得到了

one
four

但末尾有额外的空行

c
1个回答
0
投票

多次打开同一文件(使用或不使用不同的访问模式)是实现定义的。换句话说,不要这样做。

(但是,这并不意味着您不能同时读取和写入文件。)

这里有三个不错的选择。

选项 1 • 加载并保存文件

此选项分为三个步骤:

  1. 将文件完全加载到内存中。
  2. 修改内存中的数据。
  3. 用新数据覆盖原始文件。

对于普通人计划做的几乎任何事情,这个选项就足够了。它也非常方便,因为您现在对文件内容具有完整的字符串编辑功能。

变体 1 • 使用
tmpfile()
来记忆
  1. 打开源文件。
  2. 打开 tmp 文件。
  3. 阅读源代码,在写入 tmpfile 时进行更改。
  4. 用 tmpfile 数据覆盖源文件。

这实际上是我最喜欢的选择。临时文件通常只是一个内存对象,实际上并不存在于磁盘上。

选项 2 • 制作新文件,删除旧文件

这与选项 1 的变体非常相似,除了我们如何处理实际的磁盘文件。

  1. 打开源文件。
  2. 打开目标文件。
  3. 读取源代码,在写入目标时进行更改。
  4. 删除源。
  5. 将目标重命名为原始源文件名。

这是一个非常常见的选项,但我最不喜欢。然而,对于非常大的文件来说,这是唯一真正的选择。

变体 2 • 创建新的临时文件,然后复制回源文件

也就是说,不必尝试删除旧文件并重命名新文件,只需重新打开这两个文件并将新文件复制回旧文件即可。这效率较低,但克服了删除和重命名方法的一些问题。

选项 3 • 寻求读取和写入

  1. 以读写模式打开文件。
  2. 阅读直至发现要从哪里开始删除。
  3. 使用
    ftell()
  4. 获取文件指针位置
  5. 阅读直至发现要继续复制的位置。
  6. 同时(!eof)
    A。读取N个字节
    b.保存获取位置
    C。寻找放置位置
    d.写入N个字节
    e.寻求位置
  7. 截断文件

此解决方案不是最佳方案,因为所有开销簿记以及需要使用特定于操作系统的工具将文件截断为新大小。 (嗯...这是一篇关于此的帖子。)

© www.soinside.com 2019 - 2024. All rights reserved.