5

So I am opening a file that contains cards data for a card game I am designing for my assignment, basically each line contains 104 characters and each line is equal to a deck of card.

I'm using a char **** because of

  1. number of decks
  2. num of players (4)
  3. num of cards (13)
  4. card is represented like 9H, 3D means nine of hearts and three of diamonds, so it uses 2 characters.

I want to use fgets() to read multiple lines but I'm not sure if this works...

for loop is just the way how the deckfile is set, I just want to know if the fgets will go to the next line when it hits \n...

    di->cards = (char ****)malloc(sizeof(char***) * di->numDecks);
    for (i = 0; i < di->numDecks; i++) {
        di->cards[i] = (char ***)malloc(sizeof(char**) * 4);
        for (j = 0; j < 4, j++) {
            di->cards[i][j] = (char **)malloc(sizeof(char*) * 13);
            for (k = 0, k < 13, k++) {
                di->cards[i][j][k] = (char *)malloc(sizeof(char) * 3);
            }
        }
    }

    for (i = 0; i < di->numDecks, i++) {
        for (j = 0; j < 13, j++) {
            for (k = 0; k < 4; k++) {
                while ((fgets(cards[i][k][j], 3, di->deckFile)) != NULL);
            }
        }
    }
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Kevin Ko
  • 51
  • 1
  • 1
  • 2

4 Answers4

6

fgets() is often called in a loop, such as this:

FILE *fp;
char buf[260];// or char *buf, then use malloc - make index size appropriate length for anticipated line len.
fp = fopen("C:\\somedir\\card.txt", "r");
while(fgets(buf, sizeof(buf), fp)) //where sizeof(buf) is the length of   
                                   //line you anticipate reading in.
{
    //do something with buf here;
    //The input of fgets will be NULL as soon 
    //as its input fp has been fully read, then exit the loop  
}
fclose(fp);  

Your statement while((fgets(cards[i][k][j], 3, di->deckFile)) != NULL);
has a couple of issues, one is the ; at the end. It will just loop on this one line, and not give you a chance to do anything with the line that is read before it reads the next one. Also, 3 is probably not the length of line you want to read, is it? 3 is the buffer size that will hold your card data, but the line you read from the file will be longer.

So, in addition to these points, consider the other ideas in the comments, and make changes as indicated.

[EDIT] modified to read a file with "AS3D4C...(52 cards)" 4 lines
It will fill in enough spaces for 4 decks of cards. You can use this to
see how to read in the data. strtok (used before) works only when there
are delimiters, which if you can, I would recommend using instead of
long strings. Hope this helps.

(Note, I used neither malloc() or calloc() in this example.

#include <ansi_c.h>
#define FILENAME "C:\\dev\\play\\cards.txt"

int main()
{
    int i, j;    
    FILE *fp;
    char buf[260];// or char *buf, then use malloc - make index size appropriate length for anticipated line len.
    char *cardTok;
    char cards[208][3]; //4 players, 4 decks, each card is 3 bytes (eg. [A|S|\0], they all need a null termination)
    
    memset(cards, 0, 208*3);
    
    fp = fopen(FILENAME, "r");
    j = 0;
    while(fgets(buf, sizeof(buf), fp)) //where buf len is initialized at 260   
                                       //and well over the anticipated 104/line, including \n etc.
    {                          //note, fgets() will read up to n-1 characters and place a \0 at the end
                           //but will stop reading sooner if it sees end of line.
        for(i=0;i<52;i++) //i is card number
        {
            cards[i+j][0] = buf[2*i+0]; 
            cards[i+j][1] = buf[2*i+1];
            cards[i+j][2] = 0;
        }
        j+=52;
    }
    fclose(fp);
}  

My text file looked like this:

9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4SQhKD
6C9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4SQh
2D9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4SQh
3S9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4S
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • basically the data on one line is just... AH9D3C4S.....(104 char, well 52 cards and each card represent by a num+suit (A J Q K T 9 8 7 6 5 4 3 2 + Diamond Cloth Heart Spade) and every 2 char is for a player, so e.g. AH = Player1, 9D = Player2, 3C = Player3 4S = player4. so im thinking what i should do to go make fgets go to second line... – Kevin Ko Oct 16 '13 at 14:31
  • Okay, just saw this. See my edit then (in a few minutes). Also my cards.txt does not really contain a complete set of cards, I made a few, then cut and pasted. – ryyker Oct 16 '13 at 14:39
  • 1
    Dont't you want "//where 259 is the length of line with its \n you anticipate"? A buffer of 260 will nicely read up to 258 characters, the \n and then need 1 more for the \0. – chux - Reinstate Monica Oct 16 '13 at 15:17
  • @chux - yes, thanks for pointing that out. did not update comments to match my edits before, just did. Used a completely different approach last time, assuming file contained delimiters – ryyker Oct 16 '13 at 16:17
  • 1
    @ryyker OK for here, but note: ran into past issues where code used things like `fgets(buf, 120, fp)`, but the `buf` size later changed to < 120 and `fgets()` did not get updated. Thus I promote `fgets(buf, sizeof buf, fp)`. – chux - Reinstate Monica Oct 16 '13 at 16:25
  • @chux - Thanks, Thats a keeper. Will change it now. – ryyker Oct 16 '13 at 16:29
  • 1
    @chux and if `buf` is dynamically allocated? – This isn't my real name Oct 16 '13 at 18:18
  • 1
    @ElchononEdelson - would depend on initializer for `malloc()`, would it not?. eg. `char *buf; buf = malloc(260);`, then `while(fgets(buf, 260, fp);`. I believe @chux was just making sure I, and OP understood danger of changing variable lengths, without changing middle argument of `fputs()`, and offering way to mitigate. – ryyker Oct 16 '13 at 18:42
  • @ryyker chux is promoting the use of `fgets(buf, sizeof buf, fp)`, because that way if some coder changes the size of the buffer, you don't have to change every call to `fgets()`. So, what happens if there's a dynamically allocated buffer? – This isn't my real name Oct 16 '13 at 18:47
  • 1
    @ElchononEdelson - That would have to be taken into account. As I mentioned above, I believe coder would have to include initializers used in `[mc]alloc()` statements, each time `fgets()` is used. I have had to do that when using `realloc()` in a loop when reading files of varying size or content into variables created using `char *variableSizeBuf;`. I would also carry along an `unsigned int bufSize`, assign the value, and use for both `[cm]alloc()` and `fgets()`. – ryyker Oct 16 '13 at 18:57
  • 2
    @ryyker @ElchononEdelson E.g. `size_t Len; buf = malloc(Len); fgets(buf, Len, stdin);` In any case, the value used to setup mememory for `buf`, either via `buf[Len]` or `buf = malloc(Len)` is defined only once, like `Len = 120;` or `#define Len (120)` is then used in the declaration of `buf` and the `fgets(buf, Len, ...)` call. Avoid putting the magic number in `fgets()`. – chux - Reinstate Monica Oct 16 '13 at 19:07
3
    #include <stdio.h>
    char *fgets(char *s, int size, FILE *stream);

fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline.

be careful with this : If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.

When you want to compare line , before you need to remove \n before null byte.

If you want to read single line.

char line[100]; // here you can use char *line=malloc(100);

fgets(line,sizeof line,file_stream);
printf("%s\n",line);

if you want to read multiple lines

char lines[20][100]; // here you can use char **lines=malloc(100);
i=0;
//if you use **lines allocate size for all lines with the loop or else you can allocate size inside loop and then read.
while((fgets(lines[i],SIZE_ALLOCATED_FOR_LINE,file_stream)!=NULL) && (i<20))
    {
    printf("%s\n",line[i++]);
    }
Gangadhar
  • 10,248
  • 3
  • 31
  • 50
2

The documentation says,

char *fgets( char          *str, int count, FILE          *stream );
char *fgets( char *restrict str, int count, FILE *restrict stream );

Reads at most count - 1 characters from the given file stream and stores them in str. The produced character string is always NULL-terminated. Parsing stops if end-of-file occurs or a newline character is found, in which case str will contain that newline character.

Also,

The return value is NULL on failure.

If the failure has been caused by EOF condition, additionally sets the eof indicator (see feof()) on stdin. If the failure has been caused by some other error, sets the error indicator (see ferror()) on stdin.

Also check for feof to ensure NULL was obtained due to EOF

Suvarna Pattayil
  • 5,136
  • 5
  • 32
  • 59
0

If you want to take the fgets input and input all of it into an array of arrays or string array how could you do that. I have tried different things but get seg faults