H_B
H_B

Reputation: 37

Reading a file and save data into 2D array

I have a text file and want to save its content into a 2D array, so in which array[i][] I will save a word?

Words are separated with spaces or '\n'

This is the code I made but I always have a segmentation fault. When I copied the data into 1D array everything is going well but when I tried the same thing with the 2D dimension, the code explodes in my face.

Any idea why and would you help me to figure out how to manage my code so it works

I declared the 2D array this way:

char WordsInADictionary [3000][50] = {0};

My function is:

void monterDicoEnMemoire(FILE* fichierDictionary, char **WordsInADictionary) {

    int i = 0, j = 0;
    int caractereIn = 0;

    caractereIn = fgetc(fichierDictionary); 

    while (caractereIn != EOF) { 
        if (caractereIn != '\n' || caractereIn != ' ') {
            WordsInADictionary [i][j] = caractereIn;
            ++j;
        } else { 
            j = 0;
            ++i;
        }

        caractereIn = fgetc(fichierDictionary);
    }
} 

Upvotes: 1

Views: 185

Answers (2)

David C. Rankin
David C. Rankin

Reputation: 84541

You have two problems. char** WordsInADictionary is incompatible with char (*WordsInADictionary)[50]. They are different types. Your parameter is a pointer-to-pointer to char. If you dereference it, you get a simple pointer.

On the other hand, your 2D array when passed, decays to a pointer-to-array of char[50]. (a 2D array is just a 1D array of arrays) See: C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3). If you dereference it, you get a array of char [50].

Since the type controls pointer arithmetic, each j is moving by 1-byte, but when you increment i you move by 8-bytes instead of 50-bytes required to reach the beginning of the next 1D array.

To fix the problem,

void monterDicoEnMemoire (FILE* fichierDictionary, char (*WordsInADictionary)[50] ) {

If you will use each array[i] as a string, add:

            } else { 
                WordsInADictionary [i][j] = 0;   /* nul-terminate word */
                j = 0;
                ++i;
            }

And... you will need to fix your conditional expression, See the answer of @kiranBiradar where he explains the issue there.

If the words cannot contain spaces, a simple way to read them into the array would be:

#define ROWS 3000
#define COLS 50
...
size_t monterDicoEnMemoire (FILE* fichierDictionary, 
                            char (*WordsInADictionary)[COLS])
{
    size_t i = 0;

    while (i < ROWS && 
            fscanf (fichierDictionary, "%49s", WordsInADictionary[i]) == 1)
        i++;

    return i;    /* return the number of words in array */
}

(always choose a meaningful return)


Edit Following Comment of Continued Problems

Given your comment:

I've tried the code with fscanf cause it's really easy to write ans simple to understand. but when I try with a printf to check the data. I found that first thing it works only for the first word, with

printf("dico actuel: %s\n", WordsInADictionary[ 0 ]); 

if I tried a printf on WordsInADictionary[ 1 ] and so on .. it's always an empty word, second point, this warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘char **’ [-Wformat=] while (i < ROWS && fscanf(fichierDictionary, "%49s", WordsInADictionary [ i ] ) == 1)

You have something you have not implemented correctly. The printf you show is fine and should work (so long as you added it to the monterDicoEnMemoire function. The warning: is not possible if you have made the changes to provide:

size_t monterDicoEnMemoire (FILE* fichierDictionary, 
                            char (*WordsInADictionary)[COLS]) {
    ...
    while (i < ROWS &&  /* protect array bounds while reading into array */
            fscanf (fichierDictionary, "%49s", WordsInADictionary[i]) == 1)
        i++;

To help you step through the problem I wrote a quick example using what is posted above:

#include <stdio.h>

#define ROWS 3000   /* if you need a constant, #define one (or more) */
#define COLS 50

size_t monterDicoEnMemoire (FILE* fichierDictionary, 
                            char (*WordsInADictionary)[COLS])
{
    size_t i = 0;       /* word counter */

    while (i < ROWS &&  /* protect array bounds while reading into array */
            fscanf (fichierDictionary, "%49s", WordsInADictionary[i]) == 1)
        i++;

    return i;   /* return the number of words read */
}

int main (int argc, char **argv) {

    char array[ROWS][COLS] = { "" };    /* 2D array to hold words */
    size_t n = 0;                       /* number of words read */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    if ((n = monterDicoEnMemoire (fp, array)))  /* read into array */
        for (size_t i = 0; i < n; i++)          /* output each word */
            printf ("array[%2zu] : %s\n", i, array[i]);

    if (fp != stdin)    /* close file if not stdin */
        fclose (fp);
}

Example Input File

The following is a 20-word dictionary file, with one-word, and multiple-words per-line:

$ cat dat/dictwrds20.txt
a
AAA
AAAS
aardvark Aarhus Aaron ABA
Ababa
aback
abacus abalone abandon abase
abash
abate abbas abbe
abbey
abbot
Abbott

Example Use/Output

When you run the program providing the filename to read as the first argument to your program (or reading from stdin by default if no argument is given), you will get:

$./bin/readdict dat/dictwrds20.txt
array[ 0] : a
array[ 1] : AAA
array[ 2] : AAAS
array[ 3] : aardvark
array[ 4] : Aarhus
array[ 5] : Aaron
array[ 6] : ABA
array[ 7] : Ababa
array[ 8] : aback
array[ 9] : abacus
array[10] : abalone
array[11] : abandon
array[12] : abase
array[13] : abash
array[14] : abate
array[15] : abbas
array[16] : abbe
array[17] : abbey
array[18] : abbot
array[19] : Abbott

Look through what you have and see if you can identify where your code varies from the example above.

My Guess At Why Your printf Failed

Given your description in the comments, when you added the printf, I suspect your forgot to add enclosing braces ({ ... }) to create a multi-line block below the while loop. If my guess is correct, you would have needed to do:

    while (i < ROWS &&  /* protect array bounds while reading into array */
            fscanf (fichierDictionary, "%49s", WordsInADictionary[i]) == 1) {
        printf ("%s\n", WordsInADictionary[i]);
        i++;
    }

Since the while loop iterates over more than a single-line, you must enclose all lines within the loop in {...} -- as above. (which I simply omitted when the only statement within the while loop was i++;)

Upvotes: 2

kiran Biradar
kiran Biradar

Reputation: 12732

Few problems I see here are,

You need to use && not || in your if condition.

            if (caractereIn != '\n' || caractereIn != ' ') {

should be

        if (caractereIn != '\n' && caractereIn != ' ') {

And, you should null terminate the words as below.

      if (caractereIn != '\n' && caractereIn != ' ') {
           WordsInADictionary [i][j] = caractereIn;
           ++j;
      } else { 
           WordsInADictionary[i][j] = '\0';
           j = 0;
           ++i;
      }

Upvotes: 2

Related Questions