0

I am trying to read a file line by line and split it into words. Those words should be saved into an array. However, the program only gets the first line of the text file and when it tries to read the new line, the program crashes.

FILE *inputfile = fopen("file.txt", "r");
char buf [1024];
int i=0;
char fileName [25];
char words [100][100];
char *token;

 while(fgets(buf,sizeof(buf),inputfile)!=NULL){

        token = strtok(buf, " ");
        strcpy(words[0], token);
        printf("%s\n", words[0]);
        while (token != NULL) {


            token = strtok(NULL, "  ");
            strcpy(words[i],token);
            printf("%s\n",words[i]);
            i++;

        }

    }
1
  • Thanks that worked, but can you explain why is that? Commented Sep 16, 2017 at 10:35

3 Answers 3

1

After good answer from xing I decided to write my FULL simple program realizing your task and tell something about my solution. My program reads line-by-line a file, given as input argument and saves next lines into a buffer.

Code:

#include <assert.h>
#include <errno.h>
#define _WITH_GETLINE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define assert_msg(x) for ( ; !(x) ; assert(x) )

int
main(int argc, char **argv)
{
        FILE *file;
        char *buf, *token;
        size_t length, read, size;

        assert(argc == 2);

        file = fopen(argv[1], "r");
        assert_msg(file != NULL) {
                fprintf(stderr, "Error ocurred: %s\n", strerror(errno));
        }

        token = NULL;
        length = read = size = 0;

        while ((read = getline(&token, &length, file)) != -1) {
                token[read - 1] = ' ';

                size += read;
                buf = realloc(buf, size);
                assert(buf != NULL);

                (void)strncat(buf, token, read);
        }
        printf("%s\n", buf);

        fclose(file);
        free(buf);
        free(token);

        return (EXIT_SUCCESS);
}

For file file.txt:

that is a
text
which I
would like to
read
from file.

I got a result:

$ ./program file.txt
that is a text which I would like to read from file. 

Few things which is worth to say about that solution:

  1. Instead of fgets(3) I used getline(3) function because of easy way to knowledge about string length in line (read variable) and auto memory allocation for got string (token). It is important to remember to free(3) it. For Unix-like systems getline(3) is not provided by default in order to avoid compatibility problems. Therefore, #define _WITH_GETLINE macro is used before <stdio.h> header to make that function available.
  2. buf contains only mandatory amount of space needed to save string. After reading one line from file buf is extended by the required amount of space by realloc(3). Is it a bit more "universal" solution. It is important to remember about freeing objects allocated on heap.
  3. I also used strncat(3) which ensures that no more than read characters (length of token) would be save into buf. It is also not the best way of using strncat(3) because we also should testing a string truncation. But in general it is better than simple using of strcat(3) which is not recommended to use because enables malicious users to arbitrarily change a running program's functionality through a buffer overflow attack. strcat(3) and strncat(3) also adds terminating \0.
  4. A getline(3) returns token with a new line character so I decided to replace it from new line to space (in context of creating sentences from words given in file). I also should eliminate last space but I do not wanted to complicate a source code.

From not mandatory things I also defined my own macro assert_msg(x) which is able to run assert(3) function and shows a text message with error. But it is only a feature but thanks to that we are able to see error message got during wrong attempts open a file.

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

Comments

1

The problem is getting the next token in the inner while loop and passing the result to strcpy without any check for a NULL result.

while(fgets(buf,sizeof(buf),inputfile)!=NULL){

    token = strtok(buf, " ");
    strcpy(words[0], token);
    printf("%s\n", words[0]);

    while (token != NULL) {//not at the end of the line. yet!
        token = strtok(NULL, "  ");//get next token. but token == NULL at end of line
        //passing NULL to strcpy is a problem
        strcpy(words[i],token);
        printf("%s\n",words[i]);
        i++;
    }
}

By incorporating the check into the while condition, passing NULL as the second argument to strcpy is avoided.

while ( ( token = strtok ( NULL, "  ")) != NULL) {//get next token != NULL
    //if token == NULL the while block is not executed
    strcpy(words[i],token);
    printf("%s\n",words[i]);
    i++;
}

Comments

1

Sanitize your loops, and don't repeat yourself:


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

int main(void)
{

    FILE *inputfile = fopen("file.txt", "r");
    char buf [1024];
    int i=0;
    char fileName [25];
    char words [100][100];
    char *token;

 for(i=0; fgets(buf,sizeof(buf),inputfile); ) {
        for(token = strtok(buf, " "); token != NULL; token = strtok(NULL, " ")){
            strcpy(words[i++], token);
            }
        }
    return 0;
    }

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.