2

I'm trying to make a database that holds some datas about teams for now. The problem is that I cannot read the entered datas record by record. I want to explain it with just one example:

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

select prints on screen the specified information i.e. colors,stadium etc.

insert takes teams information for now.So, According to the select command, output should be followed:

black-rd   manunited    1878
blue-whte  chelsea      1905

But, I get

black-rd   manunited    1878   blue-whte

I've been trying to analyse my error for hours. I cannot find the error(s) and mistake(s). Thank you for all appreciated answers. By the way, I haven't maden while loop yet to insert or select commands. I'm pondering to find the error(s).

Code:

#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);
}
7
  • 1
    Line-by-line is something else than "binary". Remove the fread and stick to a text format and functions for this. Commented Dec 25, 2014 at 12:49
  • 1
    Besides the missing macros allow us to compile this, you may want to look at how you repeatedly pass fptr in without ever setting it. Its unrelated to your question, but it is none-the-less indeterminate. Even evaluating it invokes undefined behavior. That thing has no reason to even be a parameter from what I can see. Commented Dec 25, 2014 at 12:49
  • when I'm saying line by line I mean print on screen line by line so one under the other as seen at my example. Not about line by line reading Commented Dec 25, 2014 at 13:05
  • 1
    If you don't mean reading line by line, don't say that in the question title. If you want to read record by record, say so. Commented Dec 25, 2014 at 13:29
  • 1
    The problem here is most likely strtok, you can't tokenize the same string twice (and you need to learn how to use a debugger). Commented Dec 25, 2014 at 14:30

1 Answer 1

2

Working code below.

Apart from changes necessary to get it to build (primarily defining sizes for structure fields), the dump_teams() function is used to log the information from a team record, and the err_exit() function reports errors succinctly. Your error checks are unchanged. My primary objection to perror() is that you typically don't get the key information, such as the file name that wasn't opened, reported. It isn't hard to upgrade err_exit() to report the system error as well as a meaningful message (such as failed to open file "teams.bin": No such file or directory) — exercise for the OP. Those functions are used to validate the information, and to make sure unexpected spellings are detected. Note that the field values in debugging are enclosed in double angle brackets <<…info…>>; this makes it easier to spot various problems, such as leading or trailing spaces, or '\r' characters in input data. The assert() ensures that str2 is used in the two main functions. The select data formatting is made more uniform. There's a single name data_file to hold the data file name.

The key bug fix is in the selectfunc() loop, where the value of str1 is copied into buff afresh for each record.

This is a lazy way of working; it would be better to preparse the string once and then iterate over the data records using the data structure generated by the preparsing. This would matter more if there were millions of records than if there are just 2, of course.

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

Sample output

The files cmd1, cmd2 and cmd3 contain the three command lines from the question.

$ ./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
$

Note that by placing the debug information on standard error, it is easy to separate the debugging information from the normal output, as in the last run.

Sign up to request clarification or add additional context in comments.

2 Comments

thank you for your incredible solution. However, I cannot get #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); } the part. Could you explain it ?
It creates a function that behaves a little like printf() but it writes to standard error and exits with a failure status. I use some more sophisticated variants of this in most of my programs, but this a good step in the right direction and is small enough to include in SO answers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.