【问题标题】:Empty output file when trying to write a struct in尝试写入结构时为空输出文件
【发布时间】:2015-12-28 20:06:49
【问题描述】:

我有一个电话簿项目,我有一个函数可以从文件中读取结构,并将其放入结构数组中。因此,为了确保它正确读取,我将其打印到输出文件中,但输出文件的结果,

  0    (null) 6553280

我有一个包含类似数据的 CSV 文件

Ahmed,Mohamed,26 Elhoreya Street,15,Alexandria,4876321,ahmed@gmail.com
Sarah,Zaki,7 Smouha,36,Alexandria,3974542,sarah@hotmail.com

输出为空,它没有(读/写)正确,而使用调试器时它显示它正在读取。为什么?

int i;
int counter;

struct pb //main struct
{
    char Firstname[25];
    char Lastname[25];
    char street[20];
    int street_no ;
    char city[15];
    int number;
    char email[50];
};
struct pb k[1000];

void read_str(struct queue *queue)
{
    {
        counter = 0 ;
        FILE *read ;
        char filename[40];

        printf("Enter file name \n");
        scanf("%s",&filename);
        read=fopen(filename,"r");

        if (read == NULL)
            printf("Error");
        else
            while(!feof(read))
            {
                struct pb *n= malloc(sizeof(struct pb));
                fscanf(read,"%[^,],%[^,],%[^,],%d,%[^,],%d,%s\n",
                    k[counter].Firstname, k[counter].Lastname,
                    k[counter].street, &k[counter].street_no, 
                    k[counter].city, &k[counter].number, k[counter].email );
                counter++;
            }
        fclose(read);
    }
}

int main()
{
    read_str(&k);
    FILE *read ;

    read=fopen("out.txt","w");
    fprintf(read,"%s %s %s %d %s %d %s ",
        k[counter].Firstname, k[counter].Lastname, 
        k[counter].street, k[counter].street_no, k[counter].city,  
        k[counter].number, k[counter].email );

    fclose(read);

    return 0 ;
}

【问题讨论】:

  • 等等,我没有看到任何问号...问题是什么?
  • 抱歉,我编辑了问题的定义
  • void read_str(struct queue *queue) 什么是结构队列?你声明了一个 struct pb 数组
  • @Bob__ 对read_str 的调用是main() 的第一行
  • @Barmar:你是对的,仍然存在类型不匹配

标签: c file structure


【解决方案1】:

至少乍一看,fprintfmain 函数中的counter 的值在您的有效结构数组的末尾之后(因为counter++fscanf 之后),这意味着它是未定义的。

此外,我认为您想对fprintf 所有记录(structs)运行一个循环。但你没有。

您对fscanffprintf 格式说明符的排序不一致。

很明显,您的代码在 main 函数中没有做任何有用的事情。

更新

最少更正的代码:

#include <stdio.h>
int counter;

struct pb //main struct
{
    char Firstname[25];
    char Lastname[25];
    char street[20];
    int street_no ;
    char city[15];
    int number;
    char email[50];
};
struct pb k[1000];

void read_str()
{
    FILE *fin;
    char filename[40];
    counter = 0 ;
    printf("Enter file name \n");
    scanf("%s",filename);
    if((fin=fopen(filename,"r"))!=NULL)
    {
        while(!feof(fin))
        {
            fscanf(fin,"%[^,],%[^,],%[^,],%d,%[^,],%d,%s\n",k[counter].Firstname, k[counter].Lastname, k[counter].street, &k[counter].street_no, k[counter].city, &k[counter].number, k[counter].email);
            ++counter;
        }
        fclose(fin);
    }
}

int main()
{
    int i;
    FILE *fout;
    read_str();
    if((fout=fopen("out.txt","w"))!=NULL)
    {
        for(i=0; i<counter; ++i)
        {
            fprintf(fout,"%s %s %d %s %s %s %d\n",
                    k[i].Firstname, k[i].Lastname, k[i].street_no,
                    k[i].street,k[i].city,k[i].email,k[i].number );
        }
        fclose(fout);
    }
    return 0 ;
}

注意这段代码还有很多注意事项。

【讨论】:

  • 好的,我遇到了计数器问题,但我不确定正确的解决方案,我应该在函数中输入吗
  • 数字 15 和 36 是什么?您对fscanf 格式说明符的排序不一致。
  • @smlq 已编辑,但仍有 [counter] 问题,while 循环的边界条件是什么?
  • @smlq 效果很好, if((fout=fopen("out.txt","w"))!=NULL) 你为什么选择这个条件?另请注意,我仍在学习,因此如果您有时间告诉我您看到的主要警告,我将不胜感激,并提前感谢您的帮助
  • @Bob__:我知道它可以改进。我只是纠正了他获得正确输出的问题。实际上,正如您所说的全局 counter 变量,k 数组也是如此。
【解决方案2】:

除了在读取数据时不要超出结构数组的末尾之外,您可能还需要在其他几个方面修改您对代码所采用的方法。

首先,除非有令人信服的理由将数据结构声明为全局变量,否则应将其范围限制为main(),并将结构数组作为参数传递给任何需要访问数据的函数.此外,在处理程序中的常量时(例如最大电话簿条目1000),最好定义一个常量(#define MAXE 1000)或最好使用enum 来定义常量,例如:

enum { MAXE = 1000 };

(匿名enum 可以。)

您还可以通过为您的结构创建typedef 来简化您的生活,这将使结构数组作为参数更容易传递。例如,您可以向您的结构(命名或匿名)声明typedef,如下所示:

typedef struct {
    char Firstname[25];
    char Lastname[25];
    char street[20];
    int street_no ;
    char city[15];
    int number;
    char email[50];
} pb;

这将允许在main() 中进行简单声明,例如:

    pb k[MAXE] = {{{0},{0},{0},0,{0},0,{0}}};

虽然不是必需的,但最好在声明所有变量(包括结构数组)时对其进行初始化。

虽然在这种情况下,使用fscanf 读取数据文件或使用面向行的 输入函数几乎没有区别,但您通常会发现使用@987654333 一次读取一行@ 或getline,然后使用sscanf 或简单的指针将行解析为组件,这将提供更灵活和健壮的输入例程。无论您是使用fscanf 阅读还是使用fgets 阅读并使用sscanf 解析总是检查fscanfsscanf 的返回以验证成功转换的数量。

使用面向行的输入函数从输入文件中读取每一行文本的好处是它可以消除fscanf的僵化@格式字符串从文件的实际读取中提取,并允许您在将行成功读入缓冲区后处理分离值。在您的情况下使用fgets 的示例可能是:

/* read addresses from input file up to a maximum of MAXE
 * addresses. updates 'idx' pointer to hold the number of 
 * addreses read from file and returns number read
 */
size_t read_str (pb (*k)[], size_t *idx, FILE *fp)
{
    char tmp[MAXL] = {0};
    while (*idx < MAXE && fgets (tmp, MAXL, fp)) {
        // printf ("read[%zu]\n", *idx);
        if (sscanf (tmp, " %24[^,],%24[^,],%19[^,],%d,%14[^,],%d,%49[^\n]",
            (*k)[*idx].Firstname, (*k)[*idx].Lastname,
            (*k)[*idx].street, &(*k)[*idx].street_no, 
            (*k)[*idx].city, &(*k)[*idx].number, (*k)[*idx].email) != 7) {
            fprintf (stderr, "read_str() error: parse of line[%zu] failed.\n",
                     *idx);
            break;
        }
        (*idx)++;
    }

    return *idx;
}

注意还返回读取的地址条目数,让您可以衡量函数的成功/失败,并为您提供读取的条目数。读取的条目数 (idx) 也作为指向函数的指针传递,使调用函数中的条目数可用(此处为 main()),无论是否分配返回值。

除了这些初始问题之外,您还需要验证您所采取的每项操作,这些操作会对您的代码的持续运行产生影响。 (例如,所有文件打开、读取、写入等...)将这些部分放在一起并添加基本验证,并使用面向行的输入,您的任务的另一种方法可能如下所示:

#include <stdio.h>

/* constants for max input line and max entries */
enum { MAXL = 256, MAXE = 1000 };

typedef struct {
    char Firstname[25];
    char Lastname[25];
    char street[20];
    int street_no ;
    char city[15];
    int number;
    char email[50];
} pb;

size_t read_str (pb (*k)[], size_t *idx, FILE *fp);
void print_str_fmt (pb *k, size_t idx);
int print_str (pb *k, size_t idx, FILE *fp);

int main (int argc, char **argv) {

    if (argc < 3) { /* validate input/output filenames given as arguments */
        fprintf (stderr, "error: insufficient input, usage: %s infile outfile\n",
                argv[0]);
        return 1;
    }

    pb k[MAXE] = {{{0},{0},{0},0,{0},0,{0}}}; /* initialize variables */
    size_t index = 0;
    FILE *ifp, *ofp;

    if (!(ifp = fopen (argv[1], "r"))) { /* validate input file open  */
        fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
        return 1;
    }

    if (!(ofp = fopen (argv[2], "w"))) { /* validate output file open */
        fprintf (stderr, "error: file open failed '%s'\n", argv[2]);
        return 1;
    }

    if (!read_str (&k, &index, ifp)) { /* validate entries read */
        fprintf (stderr, "error: read_str - no addresses read\n");
        return 1;
    }
    fclose (ifp);   /* close input file  */

    printf ("The addresses are:\n\n");
    print_str_fmt (k, index);

    if (print_str (k, index, ofp)) {  /* validate entries written */
        fprintf (stderr, "error: print_str - no addresses read\n");
        return 1;
    }
    fclose (ofp);   /* close output file */

    return 0;
}

/* read addresses from input file up to a maximum of MAXE
 * addresses. updates 'idx' pointer to hold the number of 
 * addreses read from file and returns number read
 */
size_t read_str (pb (*k)[], size_t *idx, FILE *fp)
{
    char tmp[MAXL] = {0};
    while (*idx < MAXE && fgets (tmp, MAXL, fp)) {
        // printf ("read[%zu]\n", *idx);
        if (sscanf (tmp, " %24[^,],%24[^,],%19[^,],%d,%14[^,],%d,%49[^\n]",
            (*k)[*idx].Firstname, (*k)[*idx].Lastname,
            (*k)[*idx].street, &(*k)[*idx].street_no, 
            (*k)[*idx].city, &(*k)[*idx].number, (*k)[*idx].email) != 7) {
            fprintf (stderr, "read_str() error: parse of line[%zu] failed.\n",
                     *idx);
            break;
        }
        (*idx)++;
    }

    return *idx;
}

/* formatted print of addressbook to stdout */
void print_str_fmt (pb *k, size_t idx)
{
    size_t i;

    for (i = 0; i < idx; i++)
        printf (" %s %s\n %s No. %d\n %s, %d\n %s\n\n", 
                k[i].Firstname, k[i].Lastname, k[i].street, k[i].street_no,
                k[i].city, k[i].number, k[i].email);
}

int print_str (pb *k, size_t idx, FILE *fp)
{
    size_t i;

    for (i = 0; i < idx; i++)
        if (fprintf (fp, "%s,%s,%s,%d,%s,%d,%s\n", 
                    k[i].Firstname, k[i].Lastname, k[i].street, k[i].street_no,
                    k[i].city, k[i].number, k[i].email) < 0)
            return 1;

    return 0;
}

编译

gcc -Wall -Wextra -O3 -o bin/readstructsscanf readstructsscanf.c

测试输入

$ cat ../dat/phonebook.txt
Ahmed,Mohamed,26 Elhoreya Street,15,Alexandria,4876321,ahmed@gmail.com
Sarah,Zaki,7 Smouha,36,Alexandria,3974542,sarah@hotmail.com

使用/输出

$ ./bin/readstructsscanf ../dat/phonebook.txt foo.txt
The addresses are:

 Ahmed, Mohamed
 26 Elhoreya Street No. 15
 Alexandria, 4876321
 ahmed@gmail.com

 Sarah, Zaki
 7 Smouha No. 36
 Alexandria, 3974542
 sarah@hotmail.com

确认输出文件

$ diff ../dat/phonebook.txt foo.txt
$

与 C 中的所有问题一样,通常有很多方法可以找到正确的解决方案。希望这会给您一些关于如何使您的代码更加灵活和健壮的想法。

【讨论】:

  • 我真的没有得到 { gcc -Wall -Wextra -O3 -o bin/readstructsscanf readstructsscanf.c } 我什至无法尝试代码!
  • 如果你没有使用 Linux/gcc,那么编译字符串适用。像往常一样在您使用的任何操作系统上进行编译。但是,您应该始终使用-Wall-Wextra 编译选项来启用编译器警告,而-O3 只是优化级别3。尝试使用您正在使用的任何编译器再次编译代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-19
  • 1970-01-01
  • 1970-01-01
  • 2018-12-01
  • 2015-04-24
相关资源
最近更新 更多