【问题标题】:Code is adding extra characters代码正在添加额外的字符
【发布时间】:2013-10-31 01:19:54
【问题描述】:

我有一个函数应该读取文件,将单独的行作为单独的元素放入数组中。然后它应该遍历数组并将某些元素放在结构中的某些位置。

几乎拥有它...当我去打印结构以确保一切正常时,会出现额外的字符!

这是文件中的内容:

123
pre
45
cse
67
345
ret
45
cse
56

这就是它正在打印的内容:

123
pre
45
cse
C
67
345
ret
45
cse
8
56

代码如下:

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

struct students         //Defining structure for students
{
    int id;        //Students ID Number
    char name[30];      //Students Name
    int age;            //Students Age
    char dept[4];       //Studets Department
    int grade;          //Students Grade
};



int main()
{
    struct students list[20];
    FILE *f;
    char line[30];
    char **temp = NULL;
    int num_righ = 0;
    int id5;
    int age5;
    int grade5;
    int i, k;

    f = fopen("records.txt", "r");



    while(fgets(line, sizeof (line), f) != NULL)
    {
        if (line != NULL)
        {
            num_righ++;
            temp = (char**)realloc(temp, sizeof(char*) *num_righ);
            temp[num_righ - 1] = strdup(line);
        }
    }

    fclose(f);
    k = 0;
    i = 0;
    while (temp[i] != NULL)
    {
        id5 = atoi(temp[i]);
        list[k].id = id5;
        i++;
        strcpy(list[k].name, temp[i]);
        i++;
        age5 = atoi(temp[i]);
        list[k].age = age5;
        i++;
        strcpy(list[k].dept, temp[i]);
        i++;
        grade5 = atoi(temp[i]);
        list[k].grade = grade5;
        i++;
        k++;


    }
    for (i = 0; i < k; i++)
    {
        printf("%d\n", list[i].id);
        printf("%s", list[i].name);
        printf("%d\n", list[i].age);
        printf("%s\n", list[i].dept);
        printf("%d\n", list[i].grade);
    }
}

【问题讨论】:

  • 为什么不在阅读时处理每一行,而不是将其复制到临时数组中?您的方法占用了两倍的内存,但没有明显的好处......
  • strcpy() 不检查是否有空间。使用strcpyn()
  • @Floris 我已经考虑过如何做到这一点,这是我能想到的最佳解决方案(我还是个新手)。你会怎么做?每一行都是结构中的一个新元素,5 行后它在数组结构中开始一个新元素。
  • 我已经给出了一个如何在答案中做到这一点的例子(如下)。

标签: c arrays file struct


【解决方案1】:

需要注意的是,'C' 的十进制值为 67,而 '8' 的十进制值为 56。 您的学生数组中的部门数组太小。它正在抓取换行符,然后无法存储终止符。 printf 贯穿到等级整数,它被打印为一个字符。

编辑:相反,您的数组不是太小,但 fgets 正在抓取换行符,这会填充数组,从而防止空终止符被正确存储。

【讨论】:

  • 不一定。一种更简洁的方法是从您的输入中删除换行符,这通常是无论如何都想要的。 stackoverflow.com/questions/2693776/…
  • 为了澄清这个答案:fgets 取行加上换行符加上终止符'\0',总共五个字符。您的 dept 是一个由四个元素组成的数组。在您的struct 定义中将其放大一倍,一切都会好起来的。或者在复制之前去掉换行符。
  • 哦,您会注意到,当您从结构中打印name 时,您没有放入自己的换行符,而是使用从文件中提取的换行符。还值得注意的是,如果您在大端架构上运行,您不会以这种方式看到问题,因为您的打印例程会在非零字节之前命中整数的 0 字节,并且会看到字符串在那里终止。在这里它发生在将最低有效字节打印为字符之后。
  • @Taywee 我注意到,当我之前在那里运行它时。另外,如果我不删除换行符,并且我从文件中获取这些相同的元素(具有换行符),在结构中添加更多元素,然后将结构的所有元素打印到文件中,并不断重复这一点,换行符会增加吗?可能是一个愚蠢的问题,但我很好奇。
  • 更有可能的是,您最终会在程序尝试以成员身份读取的文件中出现空白行,这将导致其他有趣和令人兴奋的方式失败,除非您非常密切注意在哪里您希望换行,尤其是在通常使用程序中的成员时。 fgets 在它看到的第一个换行符之后停止,所以如果它用于读取一个空行,它只会将一个\n\0 放入数组中。
【解决方案2】:

以下代码解决了多个问题 - 它不仅确保字符串被“安全地”复制(使用strncpy,并以'\0' 终止字符串),而且还确保您不会创建内存中所有数据的第二个副本(不是玩具示例的问题,但为什么要从坏习惯开始)。

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

struct students         //Defining structure for students
{
    int id;        //Students ID Number
    char name[30];      //Students Name
    int age;            //Students Age
    char dept[4];       //Studets Department
    int grade;          //Students Grade
};

int main()
{
    struct students list[20];
    FILE *f;
    char line[30];
    char **temp = NULL;
    int num_righ = 0;
    int id5;
    int age5;
    int grade5;
    int i, k=0;
    char *newLine;

    f = fopen("records.txt", "r");
    int s = 0;  // this is the "state" counter - it goes from 0 to 4, then back to 0

    while(fgets(line, sizeof (line), f) != NULL)
    {
       newLine = strchr(line, '\n');
       if(newLine) *newLine='\0'; // terminate string on the newline.
        switch(s) {
          case 0:
            list[k].id = atoi(line);
            break;
          case 1:
             strncpy(list[k].name, line, 30);
             list[k].name[29]='\0'; // make sure it is terminated
             break;
          case 2:
            list[k].age = atoi(line);
            break;
          case 3:
            strncpy(list[k].dept, line, 3);
            list[k].dept[3] = '\0'; // make sure it is terminated
            break;
          case 4:
            list[k].grade = atoi(line);
            break;
        }
        s++;
        if (s == 5) {
          s = 0;
          k++; // if it's 5, go back to zero and start reading next structure
        }
      }
    fclose(f);

    for (i = 0; i < k; i++)
    {
        printf("id: %d\n", list[i].id);
        printf("name: %s", list[i].name);
        printf("age: %d\n", list[i].age);
        printf("dept: %s\n", list[i].dept);
        printf("grade: %d\n\n", list[i].grade);
    }
}

【讨论】:

  • 哇,这让我大吃一惊……哈哈,谢谢你给我看! :)
  • 另外,我注意到当你去掉换行符时,你只是去掉了最后一个元素;即使字符串未满,新行也会一直在哪里?
  • 不客气!您可以“投票”或“接受”您喜欢的答案,现在您已经拥有 15 点声望...
  • 否 - 我所做的是确保如果输入行比我要复制到的变量长,那么仍然会有一个终止 nul 字符。最好在复制之前从line 中删除新行...
  • 查看正确处理 newLine 的更新代码,并且仍然保留“防御性 nul 终止”。
猜你喜欢
  • 2021-06-22
  • 2013-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-10
  • 1970-01-01
  • 1970-01-01
  • 2018-03-24
相关资源
最近更新 更多