在C中附加输出文件

时间:2013-02-02 12:23:07

标签: c

我正在解决USACO的一个问题。在这个问题中,我必须将两个字符串作为输入并计算模数为47的数值。如果值相同,则打印GO,否则必须打印STAY。初始数值将通过取字母数字的乘积来计算(A为1,Z为26),然后使用模数计算最终数字。

我的程序正在编译时没有任何错误,第一个案例也是成功的。但问题出在第二种情况和我的文件被追加的方式。该计划如下: -

#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define MAX 6
main()
{
    int cal(char *ptr);
    int a,b;
    char *comet,*group;
    FILE *fptr;
    comet=malloc(6*sizeof(char));
    group=malloc(6*sizeof(char));
    scanf("%s",comet);
    a=cal(comet);
    scanf("%s",group);
    b=cal(group);
    fptr=fopen("ride.out","a+");                (1)
    //fptr=fopen("ride.txt","a+");              (2)
    if(a==b)
        fprintf(fptr,"GO\n");               (3)
        //printf("GO\n");                    (4)
    else
        fprintf(fptr,"STAY\n");              (5)
        //printf("STAY\n");                   (6)   
    fclose(fptr);
    return 0;
}
int cal(char *ptr)
{
    int c,prod=1,mod;
    while(*ptr)
        {
                c=(*ptr++)-'A'+1;
                prod=prod*c;
        }
    mod=prod%47;
    return mod;
}

输出: -

output_image

第一种情况是设置两个字符串: -

    COMETQ
    HVNGAT

,第二种情况在错误通知中给出。

如果我从(2)中删除注释符号并将其放在(1)上,那么程序工作正常,因为我可以看到文件的内容,它们就像分级系统想要的那样出现。 (1)的实际陈述没有发生。第(4)和(6)行的注释也很好,但不是第(1)行。我无法解决这个问题。有什么帮助吗?

1 个答案:

答案 0 :(得分:2)

首先注意几点:

  • main()体面的主要是:

    int main(void)
    or
    int main(int argc, char *argv[])
    
  • 使用malloc()时,您应该始终检查它是否返回NULL,又称是否失败。

  • 始终free() malloc'ed对象。
  • 每个人都有他/她/自己的编码风格。在C编码方面,我发现this to be invaluable。用它作为许多其他人的基础。点结构化代码更容易阅读,调试,解码等。

更详细的代码:

cal()的签名

main中的第一行,您声明了cal()的签名。虽然此有效,但您可能会将其放在main之上,或将cal()函数完整地放在main之上。

最大长度

您有一个永远不会使用的定义#define MAX 6。如果最多六个字符并且您读取了一个字符串,则还必须考虑尾随零。

E.g。来自cplusplus.com scanf

  
    

说明符's':任意数量的非空白字符,在找到的第一个空白字符处停止。在存储序列的末尾会自动添加一个终止空字符。

  

因此:

#define MAX_LEN_NAME    7
...
comet = malloc(sizeof(char) * MAX_LEN_NAME);

因为学习使用malloc()很好,所以在此处执行此操作时没有错误但是因为它很简单,你可能想要使用它:

char comet[MAX_LEN_NAME] = {0};
char group[MAX_LEN_NAME] = {0};

代替。至少:如果使用malloc然后检查是否成功并在完成后自由,否则使用静态数组。

更安全的scanf()

scanf()给定"%s"并不会停止读取目标缓冲区的大小 - 它继续读取将数据写入内存中的连续地址,直到它读取空白区域。 E.g:

/* data stream = "USACOeRRORbLAHbLAH NOP" */
comet = malloc(szieof(char) * 7);
scanf("%s", buf);

在记忆中我们会:

Address (example)
0x00000f           comet[0]
0x000010           comet[1]
0x000011           comet[2]
0x000012           comet[3]
0x000013           comet[4]
0x000014           comet[5]
0x000015           comet[6]
0x000016           comet[7]
0x000017           /* Anything; Here e.g. group starts, or perhaps fptr */
0x000018           /* Anything; */
0x000019           /* Anything; */
...

在阅读上面提到的流/字符串时,我们不会将USACOe读入comet,但我们会继续阅读超出范围comet。换句话说(可能)覆盖其他变量等。这可能听起来像愚蠢但由于C是低级语言,这是你必须知道的事情之一。当你了解更多时,你很可能也会学会爱它的力量:)。

为防止出现这种情况,您可以限制阅读时间,例如使用maximum length + [what to read]。 E.g:

 scanf("%6[A-Z]", comet);
         | |      |
         | |      +------- Write to `comet`
         | +-------------- Read only A to Z
         +---------------- Read maximum 6 entities

输入数据

阅读您的预期结果,错误,(N)评论等。听起来您应该有输入文件和输出文件。

现在,您的代码依赖于从标准输入读取数据,即stdin。因此,您还使用scanf()。我怀疑你应该read from file with fscanf()代替。

所以:像:

FILE *fptr_in;
char *file_data = "ride.in";
int res;
...
if ((fptr_in = fopen(file_data, "r")) == NULL) {
    fprintf(stderr, "Unable to open %s for reading.\n", file_data);
    return 1; /* error code returned by program */
}
if ((res = fscanf(fptr_in, "%6[A-Z]%*[ \n]", comet)) != 1) {
    fprintf(stderr, "Read comet failed. Got %d.\n", res);
    return 2;
}

b = cal(comet);

if ((res = fscanf(fptr_in, "%6[A-Z]%*[ \n]", group)) != 1) {
    fprintf(stderr, "Read group failed. Got %d.\n", res);
    return 2;
}

...

cal()函数

首先,命名。说这是一个项目的开始,最终会导致多个文件和数千行代码。您可能没有名为cal()的函数。学会给函数起好名字。关于编码风格的上述链接给出了一些观点。恕我直言也在小项目中这样做。这是一个很好的练习,当你写大到大的时候会更容易。将其命名为cprod_mod_47()

然后mod变量(可能是c)是多余的。另一种选择可能是:

int cprod_mod_47(char *str)
{
    int prod = 1;

    while (*str)
        prod *= *(str++) - 'A' + 1;

    return prod % 47;
}

一些更一般的建议

编译时使用许多警告和错误选项。例如。如果使用gcc说:

$ gcc -Wall -Wextra -pedantic -std=c89 -o my_prog my_prog.c

这是一个巨大的帮助。此外,使用valgrindgdb等工具也非常宝贵。