我有一个电话簿项目,我有一个函数从文件中读取结构,把它放到一个结构数组中。所以为了确保它正确读取我将它打印到输出文件但是输出文件的结果,
0 (null) 6553280
我有一个包含数据的CSV文件
Ahmed,Mohamed,26 Elhoreya Street,15,Alexandria,4876321,[email protected]
Sarah,Zaki,7 Smouha,36,Alexandria,3974542,[email protected]
输出为空,它没有(读/写)正确,而使用调试器它显示它正在读取。为什么?
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 ;
}
我至少可以乍一看,在counter
函数中fprintf
期间main
的值是在你的有效结构数组的末尾之后(因为在counter++
之后的fscanf
),这意味着它是未定义的。
而且,我认为你想要运行一个循环到fprintf
的所有记录(struct
s)。但你没有。
您对fscanf
和fprintf
格式说明符的排序不一致。
很明显,你的代码在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 ;
}
注:这段代码中仍有许多警告。
除了在读取数据时不写入结构数组的末尾之外,还有一些其他区域可能需要修改您对代码所采用的方法。
首先,除非有令人信服的理由将数据结构声明为全局变量,否则应将其范围限制为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
读取数据文件或使用面向行的输入函数之间几乎没有区别,但您通常会发现使用fgets
或getline
一次读取一行,然后使用sscanf
或简单地将该行解析为组件指针将提供更灵活和强大的输入例程。无论您是使用fscanf
阅读还是使用fgets
阅读并使用sscanf
进行解析,请始终检查fscanf
或sscanf
的返回值以验证成功转换的次数。
使用面向行的输入函数从输入文件中读取每行文本的好处是它将消除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()
here)可用,无论返回是否已分配。
除了这些初始问题之外,您还需要验证所采取的每个操作,这些操作会对代码的持续运行产生影响。 (例如,所有文件打开,读取,写入等...)将这些部分放在一起并添加基本验证,并使用面向行的输入,您的任务的另一种方法可能如下所示:
#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,[email protected]
Sarah,Zaki,7 Smouha,36,Alexandria,3974542,[email protected]
使用/输出
$ ./bin/readstructsscanf ../dat/phonebook.txt foo.txt
The addresses are:
Ahmed, Mohamed
26 Elhoreya Street No. 15
Alexandria, 4876321
[email protected]
Sarah, Zaki
7 Smouha No. 36
Alexandria, 3974542
[email protected]
确认输出文件
$ diff ../dat/phonebook.txt foo.txt
$
与C中的所有问题一样,通常有很多方法可以找到正确的解决方案。希望这会为您提供一些有关如何使代码更灵活,更健壮的其他想法。