从记录中读取二进制文件中的数据

时间:2014-12-25 12:45:46

标签: c arrays file-io struct malloc

我正在尝试建立一个数据库,其中包含一些关于团队的数据。问题是我无法通过记录读取输入的数据记录。我想用一个例子来解释它:

insert manunited,manchester,old_trafford,1878,black-rd to teams
insert chelsea,london,stamford_bridge,1905,blue-whte to teams
select colors,team_name,founding_date from teams

在屏幕上选择指定信息,即颜色,体育场等。

insert现在接受团队信息。因此,根据select命令,应该遵循输出:

black-rd   manunited    1878
blue-whte  chelsea      1905

但是,我得到了

black-rd   manunited    1878   blue-whte

我一直试图分析我的错误几个小时。我找不到错误和错误。 谢谢你们所有赞赏的答案。顺便说一句,我还没有maden while循环插入或选择命令。我正在考虑找出错误。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    char team_name[TEAM_NAME];
    char city[TEAM_NAME];
    char stadium[STADIUM_NAME];
    int  founding_date;
    char colors[COLOR];
}teams;

int main()
{

    char read_command[12];
    /*      checking insert     */
    char str1[100],str2[5],str3[18];
    FILE *fptr;
    teams t;

    scanf("%s", read_command);

    if(strcmp(read_command,"insert") == 0)
    {
        scanf("%s %s %s", str1, str2, str3);
        //printf("%s\n%s\n%s",str1,str2,str3);
        insertfunc(fptr,str1, str2, str3);

    }
    if(strcmp(read_command,"select") == 0)
    {
        scanf("%s %s %s", str1, str2, str3);
        selectfunc(fptr,str1, str2, str3);
    }

    return 0;
}

void selectfunc(FILE *fptr,char *str1,char *str2,char *str3)
{
    char *buff;
    buff =  (char*) malloc(strlen(str1) + 1);
    strcpy(buff,str1);
    char *token;

    const char comma[2] = ",";

    if(strcmp(str3,"teams") == 0)
    {
        teams t;

        fptr = fopen("teams.bin","rb");

        if( fptr == NULL )
        {
            perror("File cannot be opened.");
            exit(1);
        }
        else
        {
            while(fread(&t,sizeof(teams),1,fptr) == 1)
            {
                /* get the first token */
                token = strtok(buff, comma);

                while( token != NULL )
                {
                    if(strcmp(token,"team_name") == 0)
                    {
                        printf("  %s  ",t.team_name);
                    }
                    else if(strcmp(token,"city") == 0)
                    {
                        printf("  %s  ",t.city);
                    }
                    else if(strcmp(token,"stadium") == 0)
                    {
                        printf("  %s  ",t.stadium);
                    }
                    else if(strcmp(token,"colors") == 0)
                    {
                        printf("  %s  ",t.colors);
                    }
                    else
                    {
                        printf("  %d  ",t.founding_date);
                    }

                    token = strtok(NULL, comma);
                }
            }
        }
        fclose(fptr);
    }


}


void insertfunc(FILE *fptr,char *str1,char *str2,char *str3)
{
    char *buff;
    buff =  (char*) malloc(strlen(str1) + 1);
    strcpy(buff,str1);

    const char comma[2] = ",";

    /*
    if(buff == NULL)
        perror("error");
    */

    if(strcmp(str3,"teams") == 0)
    {
        teams t;
        int date;

        strcpy(t.team_name,strtok(buff, comma));
        strcpy(t.city, strtok(NULL, comma));
        strcpy(t.stadium, strtok(NULL, comma));
        date = atoi(strtok(NULL, comma));
        t.founding_date = date;
        strcpy(t.colors, strtok(NULL, comma));

        fptr = fopen("teams.bin","ab+");

        if( fptr == NULL )
        {
            perror("File cannot be opened.");
            exit(1);
        }
        else
        {
            fwrite(&t,sizeof(teams),1,fptr);
            fclose(fptr);
        }
    }
    free(buff);
}

1 个答案:

答案 0 :(得分:2)

下面的工作代码。

除了进行构建所需的更改(主要是定义结构字段的大小)之外,dump_teams()函数用于记录团队记录中的信息,err_exit()函数简洁地报告错误。您的错误检查未更改。我对perror()的主要反对意见是,您通常无法获取关键信息,例如未报告的文件名。升级err_exit()以报告系统错误以及有意义的消息(例如failed to open file "teams.bin": No such file or directory) - 为OP进行练习并不困难。这些功能用于验证信息,并确保检测到意外拼写。请注意,调试中的字段值用双尖括号<<…info…>>括起来;这样可以更容易地发现各种问题,例如前导或尾随空格,或输入数据中的'\r'个字符。 assert()确保str2用于两个主要功能。选择数据格式化更加统一。有一个名称data_file来保存数据文件名。

关键错误修复位于selectfunc()循环中,其中str1的值会重新复制到buff每条记录。

这是一种懒惰的工作方式;最好先预先分析字符串,然后使用预分析生成的数据结构迭代数据记录。如果有数百万条记录,那么这比仅有2条记录更重要。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { TEAM_NAME = 20, STADIUM_NAME = 20, COLOR = 20 };

static const char data_file[] = "teams.bin";

typedef struct
{
    char team_name[TEAM_NAME];
    char city[TEAM_NAME];
    char stadium[STADIUM_NAME];
    int  founding_date;
    char colors[COLOR];
} teams;

void insertfunc(char *str1, char *str2, char *str3);
void selectfunc(char *str1, char *str2, char *str3);
void err_exit(const char *fmt, ...);
void dump_teams(FILE *fp, const char *tag, const teams *team);

int main(void)
{
    char read_command[12];
    char str1[100], str2[5], str3[18];

    if (scanf("%s", read_command) != 1)
        err_exit("read for command failed");
    else if (strcmp(read_command, "insert") == 0)
    {
        if (scanf("%99s %4s %17s", str1, str2, str3) != 3)
            err_exit("read for insert failed");
        insertfunc(str1, str2, str3);
    }
    else if (strcmp(read_command, "select") == 0)
    {
        if (scanf("%99s %4s %17s", str1, str2, str3) != 3)
            err_exit("read for select failed");
        selectfunc(str1, str2, str3);
    }
    else
        err_exit("Unrecognized command <<%s>>", read_command);

    return 0;
}

void selectfunc(char *str1, char *str2, char *str3)
{
    FILE *fptr;
    char *buff;
    buff =  (char*) malloc(strlen(str1) + 1);
    char *token;

    fprintf(stderr, "select: <<%s>> <<%s> <<%s>>\n", str1, str2, str3);
    assert(strcmp(str2, "from") == 0);

    const char comma[2] = ",";

    if (strcmp(str3, "teams") == 0)
    {
        teams t;

        fptr = fopen(data_file, "rb");

        if (fptr == NULL)
        {
            perror("File cannot be opened.");
            exit(1);
        }
        else
        {
            while (fread(&t, sizeof(teams), 1, fptr) == 1)
            {
                dump_teams(stderr, "select", &t);
                strcpy(buff, str1);
                /* get the first token from command str1 */
                token = strtok(buff, comma);

                while (token != NULL)
                {
                    fprintf(stderr, "token = <<%s>>\n", token);
                    if (strcmp(token, "team_name") == 0)
                    {
                        printf("  %-20s", t.team_name);
                    }
                    else if (strcmp(token, "city") == 0)
                    {
                        printf("  %-20s", t.city);
                    }
                    else if (strcmp(token, "stadium") == 0)
                    {
                        printf("  %-20s", t.stadium);
                    }
                    else if (strcmp(token, "colors") == 0)
                    {
                        printf("  %-20s", t.colors);
                    }
                    else if (strcmp(token, "founding_date") == 0)
                    {
                        printf("  %d", t.founding_date);
                    }
                    else
                        err_exit("Unrecognized field name <<%s>>", token);

                    token = strtok(NULL, comma);
                }
                putchar('\n');
            }
        }
        fclose(fptr);
    }
    else
        err_exit("Unrecognized data source <<%s>>", str3);
}

void insertfunc(char *str1, char *str2, char *str3)
{
    FILE *fptr;
    char *buff;
    buff =  (char*) malloc(strlen(str1) + 1);
    strcpy(buff, str1);

    fprintf(stderr, "select: <<%s>> <<%s> <<%s>>\n", str1, str2, str3);
    assert(strcmp(str2, "to") == 0);

    const char comma[2] = ",";

    if (strcmp(str3, "teams") == 0)
    {
        teams t;
        int date;

        strcpy(t.team_name, strtok(buff, comma));
        strcpy(t.city, strtok(NULL, comma));
        strcpy(t.stadium, strtok(NULL, comma));
        date = atoi(strtok(NULL, comma));
        t.founding_date = date;
        strcpy(t.colors, strtok(NULL, comma));
        dump_teams(stderr, "insert", &t);

        fptr = fopen(data_file, "ab+");

        if (fptr == NULL)
        {
            perror("File cannot be opened.");
            exit(1);
        }
        else
        {
            if (fwrite(&t, sizeof(teams), 1, fptr) != 1)
                err_exit("fwrite failed");
            fclose(fptr);
        }
    }
    else
        err_exit("Unrecognized data destination <<%s>>", str3);
    free(buff);
}

void dump_teams(FILE *fp, const char *tag, const teams *team)
{
    assert(fp != 0 && tag != 0 && team != 0);
    fprintf(fp, "%s\n", tag);
    fprintf(fp, "%8s: <<%s>>\n", "Team", team->team_name);
    fprintf(fp, "%8s: <<%s>>\n", "City", team->city);
    fprintf(fp, "%8s: <<%s>>\n", "Stadium", team->stadium);
    fprintf(fp, "%8s: <<%d>>\n", "Founded", team->founding_date);
    fprintf(fp, "%8s: <<%s>>\n", "Colours", team->colors);
    fflush(fp);
}

#include <stdarg.h>

void err_exit(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    putc('\n', stderr);
    exit(EXIT_FAILURE);
}

示例输出

文件cmd1cmd2cmd3包含问题中的三个命令行。

$ ./rw < cmd1
select: <<manunited,manchester,old_trafford,1878,black-red>> <<to> <<teams>>
insert
    Team: <<manunited>>
    City: <<manchester>>
 Stadium: <<old_trafford>>
 Founded: <<1878>>
 Colours: <<black-red>>
$ ./rw < cmd2
select: <<chelsea,london,stamford_bridge,1905,blue-white>> <<to> <<teams>>
insert
    Team: <<chelsea>>
    City: <<london>>
 Stadium: <<stamford_bridge>>
 Founded: <<1905>>
 Colours: <<blue-white>>
$ ./rw < cmd3
select: <<colors,team_name,founding_date>> <<from> <<teams>>
select
    Team: <<manunited>>
    City: <<manchester>>
 Stadium: <<old_trafford>>
 Founded: <<1878>>
 Colours: <<black-red>>
token = <<colors>>
token = <<team_name>>
token = <<founding_date>>
  black-red             manunited             1878
select
    Team: <<chelsea>>
    City: <<london>>
 Stadium: <<stamford_bridge>>
 Founded: <<1905>>
 Colours: <<blue-white>>
token = <<colors>>
token = <<team_name>>
token = <<founding_date>>
  blue-white            chelsea               1905
$ ./rw < cmd3 2>/dev/null
  black-red             manunited             1878
  blue-white            chelsea               1905
$

请注意,通过将调试信息放在标准错误上,很容易将调试信息与正常输出分开,就像上次运行一样。