尝试写入结构时输出文件为空

Empty output file when trying to write a struct in

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

  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

The output is null, it doesn't (read/write) correctly, while using the debugger it shows that it's reading. Why?

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 ;
}

我至少可以第一眼看到 main 函数中 fprintf 期间 counter 的值超过了有效结构数组的末尾(因为fscanf之后的counter++),表示未定义。

此外,我想你想运行一个循环到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 ;
}

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

除了在读取数据时不要超出 array-of-structs 末尾之外,还有几个其他方面您可能希望修改您对代码采用的方法。

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

enum { MAXE = 1000 };

(匿名 enum 即可。)

您还可以通过为您的结构创建一个 typedef 来简化您的生活,这将使 array-of-structs 作为参数更容易传递。例如,您可以向您的结构(命名或匿名)声明一个 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}}};

虽然不是必需的,但在声明所有变量(包括您的 array-of-structs)时初始化它们也是一个好习惯。

而在这种情况下,使用 fscanf 或使用 line-oriented 输入函数读取数据文件之间几乎没有区别,您通常会发现使用 fgetsgetline 一次读取一行,然后使用 sscanf 或简单指针将行解析为组件将提供更灵活和健壮的输入例程。无论您是使用 fscanf 阅读还是使用 fgets 阅读并使用 sscanf 解析 总是 检查 [=27= 的 returns ] 或 sscanf 来验证成功转换的数量。

使用 line-oriented 输入函数从输入文件中读取每一行文本的好处是它会消除 fscanf format-string 来自文件的实际读取,并允许您在行成功读入缓冲区后处理分离值。在您的案例中使用 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;
}

注意 还有 return 读取的地址条目数允许您衡量 success/failure该功能以及为您提供阅读的条目数。读取的条目数 (idx) 也作为指向函数的指针传递,使调用函数中可用的条目数(此处为 main()),而不管 return 是否已分配.

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

#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

Use/Output

$ ./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 中的所有问题一样,通常有很多方法可以找到正确的解决方案。希望这会给您一些关于如何使您的代码更加灵活和健壮的想法。