2011-12-18 43 views
0

我有一个datas.txt文件:Ç - 删除/ Modifiying一条直线从一个文件

格式:姓名姓催债

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. 删除或修改此行

这是我的源代码 。

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. 我要删除 “李四” 的纪录。
  2. 我想减少“李四”的债务$ 100
+1

这一般来说相当困难。如果您想在中间进行更改,则不会绕过移动文件的大部分内容。内存映射加'memmove'可能是最简单的选择。为什么不使用数据库(比如sqlite)? – 2011-12-18 13:39:57

+0

你测试了这个代码吗?在我看来,'fscanf'这行不会做你想做的事情。也就是说,它会覆盖'name'和'surname'。 – Staven 2011-12-18 13:45:06

回答

3

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

[*]只有修改后的行长度与原始行长度相同时,才能进行修改。

编辑:PS:使用fgets,其次是sscanf(或其他一些标记行的方式)会为你节省很多的伤心。

0

通常要做的事情是读取所有文件并将其全部写回临时文件,然后删除原始文件并重命名临时文件。

/* pseudo-code!! */ 
fopen(); 
while (fscanf(source, ...)) { 
    /* massage data */ 
    fprintf(temporary, ...); 
} 
fclose(); 
remove(source); 
rename(temporary, source); 
0

为了删除或改变一条线,你必须“移动”它后面的所有东西。例如,考虑这两个文件:

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 

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

1

这有点难,因为从Unix继承而来的C文件模型(它们大部分是开发代码的)实际上并没有将文件定义为行列表。相反,它将一行定义为以换行符结尾的字符串,以及将文件(大致)定义为存储的可能长度有限的字节字符串,您可以在其中跳至不同的部分。这相当模糊,但忍受着我。

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

传统的做法有两种,取决于您可以忍受的副作用。

一种是在另一个文件中写入更新的版本,然后重新命名()到位。这样做的好处是新文件将在您使用该文件时完成,但缺点是它可能无法精确匹配旧文件,只要权限等等,并且对于其他程序不可见已经打开了旧的。如果两个程序以这种方式修改文件,则这是一个竞争条件,因为其中一个更改会被另一个覆盖。

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

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

-1

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

这样你就可以操纵数组中的数据。这也使得你的程序可以更容易地扩展,比如排序,或者只是在文件的顶部写入而无法完成的额外数学。

例如尝试编写可以读取到一个结构的功能,如:

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

然后你做什么使这些结构的阵列和操纵这些。 您将学到很多关于结构,动态内存分配的知识,而且您肯定会遇到一些有助于您学习的有趣问题。

我的建议是还跳过C和去C++ ...学习使用输入输出流,而不是*的printf这个东西/ * scanf函数的功能和载体很可能将更好地为您从长远看

+0

为什么downvotes?我错过了什么? 我认为这是一个更具可扩展性的解决方案来处理数据 – Fuzz 2011-12-21 04:21:28