Aguen Aguen - 1 month ago 6
C Question

Reading Space Delimited Strings Line by Line and Storing Them in Arrays (in C)

I have an input file consisting of:

CREATEHALL "Red-Hall" "StarDust" 24 20
CREATEHALL "Orange-Hall" "Last_Samurai" 10 20


And I want to store each line in an array for future uses.

So far my code is:
(Added lots of printf's for debugging purposes.)

#include <stdio.h>
#include <stdlib.h>
struct str
{
char *commands[5];
};

struct str a[];
int main()
{
int i=0;
int j=0;
char *token;
printf("Starting the program...\n");
char filename[] = "input.txt";
FILE *file = fopen ( filename, "r" );

if (file != NULL) {
char line [1000];
printf("Read a new line...\n");
while(fgets(line,sizeof line,file)!= NULL) /* read a line from a file */ {
j=0;
printf("%s\n",line);
printf("Start token stuff...\n");
/* get the first token */
token = strtok(line, " ");

/* walk through other tokens */
while( token != NULL )
{
a[i].commands[j]=token;
printf( "Stored Command : %s\n", token );
token = strtok(NULL, " ");
j++;
}
i++;
}

fclose(file);
}
else {
perror(filename); //print the error message on stderr.
}
printf("Finished processing tokens...\n\n\n");

printf("%s\n%s\t%s\t%s\t%s\n", a[0].commands[0], a[0].commands[1],a[0].commands[2],a[0].commands[3],a[0].commands[4]);
printf("%s\n%s\t%s\t%s\t%s\n", a[1].commands[0], a[1].commands[1],a[1].commands[2],a[1].commands[3],a[1].commands[4]);
return 0;
}


The output I get on the console is this:

Starting the program...
Read a new line...
CREATEHALL "Red-Hall" "StarDust" 24 20

Start token stuff...
Stored Command : CREATEHALL
Stored Command : "Red-Hall"
Stored Command : "StarDust"
Stored Command : 24
Stored Command : 20

CREATEHALL "Orange-Hall" "Last_Samurai" 10 20
Start token stuff...
Stored Command : CREATEHALL
Stored Command : "Orange-Hall"
Stored Command : "Last_Samurai"
Stored Command : 10
Stored Command : 20
Finished processing tokens...


CREATEHALL
"Orange-Hall" l" murai" ai"
CREATEHALL
"Orange-Hall" "Last_Samurai" 10 20


I'm not very good with C, (this is a very small part of an assignment) but I believe the error is in the

a[i].commands[j]=token;


line, since before that it reads the words fine.

Thanks in advance.

LPs LPs
Answer

From the Man

Return Value

The strtok() and strtok_r() functions return a pointer to the next token, or NULL if there are no more tokens.

So pointers you are storing are addresses of char lib[1000], that, at the end of your process, will contain the last line read.


Take also note that you defined a bad array, without dimension:

struct str a[];

should be, at least,

struct str a[2];

The fastest correction can be:

#define _BSD_SOURCE

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

struct str
{
char *commands[5];
};

struct str a[2];
int main()
{
    int i=0;
    int j=0;
    char *token;
    printf("Starting the program...\n");
    char filename[] = "input.txt";
    FILE *file = fopen ( filename, "r" );

    if (file != NULL) {
        char line [1000];
        printf("Read a new line...\n");
        while(fgets(line,sizeof line,file)!= NULL) /* read a line from a file */ {
            j=0;
            printf("%s\n",line);
            printf("Start token stuff...\n");
            /* get the first token */
            token = strtok(line, " ");

            /* walk through other tokens */
            while( token != NULL )
            {
                char *temp = strdup(token);
                if (temp != NULL)
                {
                    a[i].commands[j]=temp;
                }
                else
                {
                    fprintf(stderr, "No memory available to store string\n");
                    return 1;
                }
                printf( "Stored Command : %s\n", token );
                token = strtok(NULL, " ");
                j++;
            }
            i++;
        }

        fclose(file);
    }
    else {
        perror(filename); //print the error message on stderr.
    }
    printf("Finished processing tokens...\n\n\n");

    printf("%s\n%s\t%s\t%s\t%s\n", a[0].commands[0], a[0].commands[1],a[0].commands[2],a[0].commands[3],a[0].commands[4]);
    printf("%s\n%s\t%s\t%s\t%s\n", a[1].commands[0], a[1].commands[1],a[1].commands[2],a[1].commands[3],a[1].commands[4]);
    return 0;
}

In more complex project, duplicated strings must be freed due to heap allocation performed by strdup.


Another solution can be implemented using malloc and allocating space for each element and using strcpy to copy the token.

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

struct str
{
    char *commands[5];
};

struct str a[2];

int main()
{
    int i=0;
    int j=0;
    char *token;
    printf("Starting the program...\n");
    char filename[] = "input.txt";
    FILE *file = fopen ( filename, "r" );

    if (file != NULL) {
        char line [1000];
        printf("Read a new line...\n");
        while(fgets(line,sizeof line,file)!= NULL) /* read a line from a file */ {
            j=0;
            printf("%s\n",line);
            printf("Start token stuff...\n");
            /* get the first token */
            token = strtok(line, " ");

            /* walk through other tokens */
            while( token != NULL )
            {
                a[i].commands[j] = malloc(strlen(token)+1);
                if (a[i].commands[j] != NULL)
                {
                    strcpy(a[i].commands[j], token);
                }
                else
                {
                    fprintf(stderr, "No memory available to store string\n");
                    return 1;
                }
                printf( "Stored Command : %s\n", token );
                token = strtok(NULL, " ");
                j++;
            }
            i++;
        }

        fclose(file);
    }
    else {
        perror(filename); //print the error message on stderr.
    }
    printf("Finished processing tokens...\n\n\n");

    printf("%s\n%s\t%s\t%s\t%s\n", a[0].commands[0], a[0].commands[1],a[0].commands[2],a[0].commands[3],a[0].commands[4]);
    printf("%s\n%s\t%s\t%s\t%s\n", a[1].commands[0], a[1].commands[1],a[1].commands[2],a[1].commands[3],a[1].commands[4]);
    return 0;
}

Same thing here, you should freed mallocated memory.

Comments