我有一个电话簿的项目,我有一个函数从文件中读取结构,把它放到一个结构数组中。因此,为了确保它正确读取,我将其打印到输出文件中,但输出文件的结果
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 ;
}
答案 0 :(得分:1)
我至少可以乍一看,counter
fprintf
期间main
counter++
的价值超过了有效结构数组的结尾(因为{{ 1}}在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 ;
}
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
或使用面向行的输入函数读取数据文件之间几乎没有区别,但通常会发现一次读取一行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()
)中的条目数可用,无论返回是否已分配。
除了这些初始问题之外,您还需要验证所采取的每个操作,这些操作会对代码的持续运行产生影响。 (例如,所有文件打开,读取,写入等...)将这些部分放在一起并添加基本验证,并使用面向行的输入,您的任务的另一种方法可能如下所示:
#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中的所有问题一样,通常有很多方法可以找到正确的解决方案。希望这会为您提供一些关于如何使代码更加灵活和健壮的其他想法。