尝试在中编写结构时清空输出文件

时间:2015-12-28 20:06:50

标签: c file structure

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

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

2 个答案:

答案 0 :(得分:1)

我至少可以乍一看,counter fprintf期间main counter++的价值超过了有效结构数组的结尾(因为{{ 1}}在fscanf之后,这意味着它是未定义的。

此外,我认为你想要运行一个循环到fprintf所有记录(struct s)。但你没有。

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。这段代码中仍有许多警告。

答案 1 :(得分:0)

除了在读取数据时没有超出结构数组的末尾时,还有一些其他方面可能需要修改您对代码采用的方法。

首先,除非有令人信服的理由将数据结构声明为全局变量,否则应将其范围限制为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或使用面向行的输入函数读取数据文件之间几乎没有区别,但通常会发现一次读取一行fgetsgetline然后使用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中的所有问题一样,通常有很多方法可以找到正确的解决方案。希望这会为您提供一些关于如何使代码更加灵活和健壮的其他想法。