打印2d数组时的额外字符

时间:2014-07-29 04:48:29

标签: c

打印数组时,我无法摆脱多余的字符。我不确定我做错了什么。输出将随机字符添加到行尾。任何帮助深表感谢。

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

int main(int argc, char **argv){
    int i=0;
    char c;
    const char * ch;
    int num_lines = 0;
    int k = 0;

    FILE *fpin;
    if(argc > 1){
        fpin = fopen(argv[1], "r");  
    }
    if (fpin == NULL){
        printf("Cannot Open File");
        return(0);
    }
    while(!feof(fpin)){
        c = getc(fpin);
        if(c == '\n'){
            num_lines++;
        }
     }
    printf("%d", num_lines);

    char **result = malloc(sizeof(char) * num_lines + 2);
    for(i=0;i<=num_lines;i++){
        result[i] = malloc(1000*sizeof(char));
    }

    i=0;
    rewind(fpin);   
    while(i<num_lines){
        c = getc(fpin);
        ch = &c;
        printf("%c", c);
        if (c == '\n'){
            strncat(result[i], "\0", 1);
            i++;
        }
        if(result[i] == NULL){
            printf("bad memory");
            break;
        } else {
            strncat(result[i], ch, sizeof(char));
        }
    }

    for(i=0;i<=num_lines;i++){

        printf("%s", result[i]);

     }
}

4 个答案:

答案 0 :(得分:1)

这条线路不对。

char **result = malloc(sizeof(char) * num_lines + 2);

应该是:

char **result = malloc(sizeof(char*) * num_lines + 2);

请记住,您正在尝试分配num_lines char*,而不是num_lines char。我不确定你为什么要在那里增加+ 2。你只需要:

char **result = malloc(sizeof(char*) * num_lines);
for(i=0;i<num_lines;i++){  /* Note the use of < and not <= */
    result[i] = malloc(1000*sizeof(char));
}

您可以进一步将for循环简化为:

for(i=0;i<num_lines;i++){
    result[i] = malloc(1000); /* sizeof(char) is always 1 */
}

答案 1 :(得分:1)

其他答案指出了大部分主要问题,而@David C. Rankin在评论中提到了一个关键点:

  

调用strncat时,结果[i]不会终止。因此,strncat不知道在哪里放。

总结:

char **result = malloc(sizeof(char *) * (num_lines + 1));  // Proper allocation

for(i = 0; i <= num_lines; i++)
{
    result[i] = calloc(1000, sizeof(char));
}

我要添加的最后一点是在重建时跳过将换行符附加到行上:

while(i <= num_lines)
{
   // ...
    if(c == '\n')
    {
        i++;
    }
    else if(result[i] == NULL) // <== Note the else
    {
    // bad memory, I guess?
    }
    else
    {
        strncat(result[i], ch, 1);
    }
}

然后在打印时将其添加回来:

print("%s\n", result[i]);

答案 2 :(得分:0)

以下内存分配方式会导致segmentation fault

char **result = malloc(sizeof(char) * num_lines + 2); //Instead of sizeof(char) you should use sizeof(char *).
for(i=0;i<num_lines;i++){
    result[i] = malloc(1000*sizeof(char));
}

而不是尝试以下 -

char **result = malloc(sizeof(char *) * num_lines);
for(i=0;i<num_lines;i++){
    result[i] = malloc(1000*sizeof(char));
}

然后程序的剩余部分工作正常。无需改变任何东西。

但是当你动态分配内存时,不要忘记最后释放内存。

for(i=0;i<num_lines;i++){
    printf("%s", result[i]);
 }

free(result); // It frees the allocated memory

[注意]:当您使用命令行参数时,您应检查用户的输入,如果未提供必要的输入,则需要打印使用消息。否则会导致Segmentation fault

if(argc!=2){
printf("Usage: ./a.out Filename\n");
return 0;
}

答案 3 :(得分:0)

因此,strncat(dst, src, n)扫描dst寻找'\0',然后从n添加最多src个字符,停止在'\0'终止},然后追加'\0'。正如其他地方所述,你的问题是你没有初始化每个result[i]是一个空字符串...... calloc()会为你做的,虽然有一点点过度杀戮。

除此之外:了解C确实没有字符串类型非常重要。它具有的是字符值数组,以及字符值'\0'是“字符串终止符”的约定。使用此约定,strxxx()函数在char数组上执行有用的“字符串式”操作......但是当您使用它们时,值得记住“没有字符串”。 (有很深的魔力允许指针和数组非常接近,但不完全是彼此的双重。这使得char*看起来更像是一个“字符串”,但它只是一种幻觉,无论多么方便有用的可能是......这里有薄冰,如果你不小心,你就会遇到麻烦。)

现在,对于每个字符读取,您的代码都会执行strncat(),它将重新扫描当前result[i] ...寻找'\0',我在这里告诉你是O(n ^ 2):-((除非一些聪明的代码缓存了最后的已知长度。)

此外,对于超过1000个字符的行(包括其终止'\0'),您将超出result[i]的结尾。

除此之外: C给人类最大的礼物就是你可以轻松地覆盖一系列的char :-(每次你触摸一个“字符串”时你都需要问自己“这可以吗?”超支?“,然后当你认为你有答案时,再问一遍”这可能会超出考虑到最后的'\0' - 并且集中精力于“字符串”的长度之间的差异(其中不包括'\0')和char数组的长度(确实如此)。

此外,如果输入中出现'\0',那么(有效地)会切断线的其余部分。当然,你可能有理由相信这种情况永远不会发生。 (当不可能的事情发生时,很多代码已经崩溃并烧毁了!)

正如其他地方所述,你计算'\n'的数量,并且(排序)加1来给你行数,除非文件以'\n'结尾,否则这很好。您可以检查扫描循环中读取的最后一个字符是否为'\n',以判断您是否有一个尾随的,未终止的行。

这是另一个教训......集中(努力)处理边缘情况和循环终止条件......你的读取循环是:

while(i<num_lines){
  ....
  if (c == '\n'){
    ....
    i++;
  }
....
}

因此,由于num_lines实际上是'\n'的数量,因此不会读取尾随(未'\n'行)(如果有的话)。

另一方面,您的输出循环具有微妙的不同终止条件:

for(i=0;i<=num_lines;i++){
  printf("%s", result[i]);
}

如果result[num_lines]已被初始化为空,那将会很好!

对于读取循环,您可以:

l = 0 ;
i = 0 ;
while (!feof(fpin)) {
    c = getc(fpin);
    if (c == '\n') {
        result[i][l] = '\0' ;
        i += 1 ;
        l  = 0 ;
      }
    else if ((c != '\0') && (l < (1000 - 1))) {
        result[i][l] = c ;
        l += 1 ;
      }
  }

if (l != 0)
    num_lines += 1 ;

请注意,最后l == 0表示文件以'\n'结尾(可能后跟任意数量的'\0'),相反l != 0表示存在not-empty尾随行未被'\n'终止(忽略任何'\0')。这可能很有用 - 虽然不是在你的情况下,在任何情况下你输出的结果都少于'\n'

这会忽略输入中的'\0'并将行截断为999个字符。你可能想要做一些事情来更彻底地处理这些案件。