C - 从文件中删除/修改一行

时间:2011-12-18 13:30:22

标签: c file-io

我有一个 datas.txt 文件:

格式:姓氏债务偿还

bir bir 100 2 
iki iki 200 2 
eray alakese 100 5 
john doe 2000 10 

我正在学习C和我只知道简单的文件功能(fscanf,fprinf,fopen等)

我会

  1. 使用scanf询问用户名和姓氏,然后将其分配给名称姓氏变量。
  2. 它将搜索名称姓氏的文件,然后将债务和付款分配给债务付款变量(fscanf(file, "%s %s %d %d", name, surname, &debt, &payment);
  3. 删除或修改此行
  4. 这是我的源代码。

        scanf("%s", &name);
        scanf("%s", &surname);
        file = fopen("datas.txt", "r");
        /* this fscanf() is working as expected. There is no problem. */
        fscanf(file, "%s %s %d %d", name, surname, &debt, &payment);
    
        /* modify and delete actions here */
        fclose(file);
    

    示例:

    1. 我想删除“John Doe”的记录。
    2. 我想把“John Doe”的债务减少到100美元

4 个答案:

答案 0 :(得分:3)

您不能删除/修改[*]文本文件的各行;唯一的解决方案是1)创建一个新的临时文件,2)复制内容,但不包括你想要修改/删除的行,3)输出修改后的行,4)复制原始文件的其余部分,5)用临时文件替换旧文件。

[*]仅当修改的行与原始行具有相同的长度时才可以进行修改。

编辑:PS:使用fgets,然后使用sscanf(或其他一些标记线的方式)将为您带来更多的悲伤。

答案 1 :(得分:1)

这有点难,因为从Unix继承的C文件模型(它们主要是代码开发的)实际上并没有将文件定义为行列表。相反,它将一行定义为以换行符终止的字节串,将文件(粗略地)定义为可能有限长度的存储字节串,您可以跳到不同的部分。这是相当模糊的,但请耐心等待。

当我们尝试将我们的想法 - “修改此行”,“删除该行” - 转换为文件操作时,问题变得更加清晰。我们可以通过在换行符处停止读取一行,但根本没有命令将其切割成多个部分;只设置结束(ftruncate())。因此,要更改行的大小,我们需要复制其后的所有数据。它可以完成,但重新创建文件通常更容易。比较实现memmove()的细微之处。

执行此操作的传统方法有两种变体,具体取决于您可以忍受的副作用。

一种是将更新后的版本写入另一个文件,然后将其重命名()到位。这样做的好处是,新文件将在您放置到位时完成,但缺点是它可能与旧文件不完全匹配,只要权限等,并且它将不会被其他程序看到已经把旧的打开了。如果两个程序像这样修改文件,则它是竞争条件,因为其中一个更改被另一个更改。

另一种方法是完全加载数据并将修改后的版本写入到位。这意味着文件本身保留在适当的位置,权限和所有权限,但是当您保存它是新旧内容的混合时会有一段时间。文本编辑器倾向于这样做,通常将旧内容保存为单独的文件,以防出现问题。

还有管理副作用的工具,例如版本化文件系统,文件锁定,甚至为并行更改准备的库(想到metakit)。大多数时候我们都会使用已经存在的工具,比如sed -i。

答案 2 :(得分:0)

为了删除或改变一行,你必须" shift"一切都在它之后。例如,请考虑以下两个文件:

bir bir 100 2           bytes 0-14
iki iki 200 2           bytes 15-29
eray alakese 100 5      bytes 30-49
john doe 2000 10        bytes 50-67

bir bir 100 2           bytes 0-14
iki iki 200 2           bytes 15-29
john doe 2000 10        bytes 30-57  <-- byte offsets have changed

这当然是可以做到的,但一般来说支持起来非常复杂(你必须做很多寻求和讲述)。更常用的方法是有效地复制文件:从输入文件读入并将所有内容打印到输出文件,进行所需的修改。 (例如,要删除&#34;删除&#34;一行,您只是不打印该行。)然后,最后,在关闭这两个文件后,您将重命名&#34;输出文件覆盖输入文件。这是命令行实用程序(如sedperl)在指示修改文件&#34;就地&#34;时使用的方法。

答案 3 :(得分:-1)

我通常处理这样的事情的方法是编写一个可以“读入”数据并将其存储到某个结构的函数。然后是一个将数据从结构写入文件的函数。

这样您就可以操纵数组中的数据。这也使得您的程序更易于进行排序或额外数学操作,只需在文件顶部写入即可完成。

e.g。尝试编写一个可以读入结构的函数,如:

struct Client
{
    char name[255];
    double owes;
    double paid;
}

然后你做了一些这些结构的数组并操纵它们。 你将学到很多关于结构,动态内存分配的知识,毫无疑问你会遇到一些有助于你学习的有趣问题。

我的建议是跳过C并转向C ++ ...从长远来看,使用iostream而不是* printf / * scanf函数和向量学习这些东西对你来说可能会更好