file input int strings c [duplicate] - c

This question already has answers here:
Going through a text file line by line in C
(4 answers)
Closed 8 years ago.
So I'm having issues right now with my program. I'm trying to get it to open a file count the lines rewind and then go through the file to store the variables.
String String String int is the format of the file but I'm having issues after I count the lines. I can print the numbers to the screen but then I get a seg fault right after the print. I don't know why
int countLines(FILE * fin){
int count=0;
char street[100];
char city[100];
char state[3];
int zip;
do{
fgets(street, 100, fin);
fgets(city, 100, fin);
fgets(state, 3, fin);
fscanf(fin, "%d\n", &zip);
count++;
}while(!feof(fin));
rewind(fin);
return count;
}
lines=countLines(fin); is how I call the function. What am I doing wrong?

Do not mix fgets() with fscanf() until you are very comfortable with these functions. They do not play well together. That \n in the format is a white space and will match any number of consecutive white space including multiple \n, spaces, tabs, etc.
// fscanf(fin, "%d\n", &zip);
Recommend avoiding feof() and using the return value from fgets().
feof() does not become true until a file read is attempted and fails to provide a char. This is different than "true when none left". Example: you read the last char of a file. feof() is still false. Code attempts to read more (and fails). Now feof() is true. (and remains true).
Do count the lines in a simple fashion and use symmetry. Further consider more error checking. Read the zip code line as a string and then parse it as an integer.
int countLines(FILE * fin){
int count=0;
char street[100];
char city[100];
char state[100];
char zips[100];
unsigned zip;
while (fgets(street, sizeof street, fin) != NULL) {
count++;
}
rewind(fin);
if (count%4 != 0) Handle_LineCountNotMultipleof4();
// You could return here, but let's read the file again and get the data.
// This is likely part of OP's next step.
for (int i=0; i<count; i += 4) {
if ((NULL == fgets(street, sizeof street, fin)) ||
(NULL == fgets(city, sizeof city, fin)) ||
(NULL == fgets(state, sizeof state, fin)) ||
(NULL == fgets(zips, sizeof zips, fin)) ||
(1 != sscanf(zips, "%u", &zip))) handle_error();
// Remember street, city, state, still have an ending \n
do_something(street, city, state, zip);
}
return count;
}
Alternatively, to count the lines use the following. A singular difficulty occurs in reading if you have long lines, so let's check that as we go. Take this out line length stuff if you prefer a simple answer. You could use the Maxline+1 as you buffer size instead of a fixed 100.
size_t Maxline = 0;
size_t Curline = 0;
int ch;
while ((ch = fgetc(fin)) != EOF) {
Curline++;
if (ch == '\n') {
count++;
if (Curline > Maxline) MaxLine = Curline;
Curline = 0;
}
}
if ((Maxline + 1) > 100) TroubleAhead() ; // Trouble with future (fgets(buf, 100, fin), use bigger buffers
rewind(fin);

fgets tries to read a whole line,not just a word, which seems to be what you are hoping for.
So, the buffers you are passing it arent big enough, and they won't get what you were hopign for and you are incrementign the count once for every 3 lines and fscan usually wont read a line leaving the remains of that line to interfere with your next read.
If you want to read words, try scanf, with %s. If you want to read a fixed number of chars, try fread.

Related

Comparing two files char by char - end of file error in C

I have a specific problem. I have to read Strings from two files and compare them char by char, and tell in which row is the difference and for how many chars they differ. I have made pointer and all stuff, but the problem is when it comes to the end of the first file (it needs to read just for the length of the shorter file) I am unable to compare the last String because my for loop goes till the '\n', and in last row there is no \n. But on the other side if I put '\0' in for loop it gives me a wrong result, because it counts '\n' as char as well. How do I handle this problem? Suggestions? I don't want to use strcmp() since I need to count char difference, and there are some other conditions to be fulfilled. Here is my problem:
while(!feof(fPointer)){
zeichnen = 0;
fgets(singleLine, 150, fPointer);
fgets(singleLine2, 150, fPointer2);
length = strlen(singleLine); // save the length of each row, in order to compare them
length2 = strlen(singleLine2);
if(length <= length2){
for(i=0; singleLine[i] != '\n'; i++){
singleLine[i] = tolower(singleLine[i]);
singleLine2[i] = tolower(singleLine2[i]);
if(singleLine[i] != singleLine2[i]){
zeichnen++;
}
}
printf("Zeile: %d \t Zeichnen: %d\n", zeile, zeichnen); // row, char
for(i=0; i < min(length, length2); i++) {
if ( singeLine[i] == '\n' || singleLine2[i] == '\n')
break;
/* Do work */
}
Alternative is to use logical and operator to test two or more conditions in for loop conditional part.
From fgets manual page:
char *fgets(char *s, int size, FILE *stream);
fgets() returns s on success, and NULL on error or when end of file
occurs while no characters have been read.
So you chould have while(true) loop and check both fgets return values for end of input.

How to take a line input in C?

I was trying to take a full line input in C. Initially I did,
char line[100] // assume no line is longer than 100 letters.
scanf("%s", line);
Ignoring security flaws and buffer overflows, I knew this could never take more than a word input. I modified it again,
scanf("[^\n]", line);
This, of course, couldn't take more than a line of input. The following code, however was running into infinite loop,
while(fscanf(stdin, "%[^\n]", line) != EOF)
{
printf("%s\n", line);
}
This was because, the \n was never consumed, and would repeatedly stop at the same point and had the same value in line. So I rewrote the code as,
while(fscanf(stdin, "%[^\n]\n", line) != EOF)
{
printf("%s\n", line);
}
This code worked impeccably(or so I thought), for input from a file. But for input from stdin, this produced cryptic, weird, inarticulate behavior. Only after second line was input, the first line would print. I'm unable to understand what is really happening.
All I am doing is this. Note down the string until you encounter a \n, store it in line and then consume the \n from the input buffer. Now print this line and get ready for next line from the input. Or am I being misled?
At the time of posting this question however, I found a better alternative,
while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
{
printf("%s\n", line);
}
This works flawlessly for all cases. But my question still remains. How come this code,
while(fscanf(stdin, "%[^\n]\n", line) != EOF)
{
printf("%s\n", line);
}
worked for inputs from file, but is causing issues for input from standard input?
Use fgets(). #FredK
char buf[N];
while (fgets(buf, sizeof buf, stdin)) {
// crop potential \n if desired.
buf[strcspn(buf, "\n")] = '\0';
...
}
There are to many issues trying to use scanf() for user input that render it prone to mis-use or code attacks.
// Leaves trailing \n in stdin
scanf("%[^\n]", line)
// Does nothing if line begins with \n. \n remains in stdin
// As return value not checked, use of line may be UB.
// If some text read, consumes \n and then all following whitespace: ' ' \n \t etc.
// Then does not return until a non-white-space is entered.
// As stdin is usually buffered, this implies 2 lines of user input.
// Fails to limit input.
scanf("%[^\n]\n", line)
// Does nothing if line begins with \n. \n remains in stdin
// Consumes 1 char after `line`, even if next character is not a \n
scanf("%99[^\n]%*c", line)
Check against EOF is usual the wrong check. #Weather Vane The following, when \n is first entered, returns 0 as line is not populated. As 0 != EOF, code goes on to use an uninitialized line leading to UB.
while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
Consider entering "1234\n" to the following. Likely infinite loop as first fscanf() read "123", tosses the "4" and the next fscanf() call gets stuck on \n.
while(fscanf(stdin, "%3[^\n]%*c", line) != EOF)
When checking the results of *scanf(), check against what you want, not against one of the values you do not want. (But even the following has other troubles)
while(fscanf(stdin, "%[^\n]%*c", line) == 1)
About the closest scanf() to read a line:
char buf[100];
buf[0] = 0;
int cnt = scanf("%99[^\n]", buf);
if (cnt == EOF) Handle_EndOfFile();
// Consume \n if next stdin char is a \n
scanf("%*1[\n]");
// Use buf;
while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
worked for inputs from file, but is causing issues for input from standard input?
Posting sample code and input/data file would be useful. With modest amount of code posted, some potential reasons.
line overrun is UB
Input begins with \n leading to UB
File or stdin not both opened in same mode. \r not translated in one.
Note: The following fails when a line is 100 characters. So meeting the assumption cal still lead to UB.
char line[100] // assume no line is longer than 100 letters.
scanf("%s", line);
Personally, I think fgets() is badly designed. When I read a line, I want to read it in whole regardless of its length (except filling up all RAM). fgets() can't do that in one go. If there is a long line, you have to manually run it multiple times until it reaches the newline. The glibc-specific getline() is more convenient in this regard. Here is a function that mimics GNU's getline():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
long my_getline(char **buf, long *m_buf, FILE *fp)
{
long tot = 0, max = 0;
char *p;
if (*m_buf == 0) { // empty buffer; allocate
*m_buf = 16; // initial size; could be larger
*buf = (char*)malloc(*m_buf); // FIXME: check NULL
}
for (p = *buf, max = *m_buf;;) {
long l, old_m;
if (fgets(p, max, fp) == NULL)
return tot? tot : EOF; // reach end-of-file
for (l = 0; l < max; ++l)
if (p[l] == '\n') break;
if (l < max) { // a complete line
tot += l, p[l] = 0;
break;
}
old_m = *m_buf;
*m_buf <<= 1; // incomplete line; double the buffer
*buf = (char*)realloc(*buf, *m_buf); // check NULL
max = (*m_buf) - old_m;
p = (*buf) + old_m - 1; // point to the end of partial line
}
return tot;
}
int main(int argc, char *argv[])
{
long l, m_buf = 0;
char *buf = 0;
while ((l = my_getline(&buf, &m_buf, stdin)) != EOF)
puts(buf);
free(buf);
return 0;
}
I usually use my own readline() function. I wrote this my_getline() a moment ago. It has not been thoroughly tested. Please use with caution.

Reading multiple lines with different data types in C

I have a very strange problem, I'm trying to read a .txt file with C, and the data is structured like this:
%s
%s
%d %d
Since I have to read the strings all the way to \n I'm reading it like this:
while(!feof(file)){
fgets(s[i].title,MAX_TITLE,file);
fgets(s[i].artist,MAX_ARTIST,file);
char a[10];
fgets(a,10,file);
sscanf(a,"%d %d",&s[i].time.min,&s[i++].time.sec);
}
However, the very first integer I read in s.time.min shows a random big number.
I'm using the sscanf right now since a few people had a similar issue, but it doesn't help.
Thanks!
EDIT: The integers represent time, they will never exceed 5 characters combined, including the white space between.
Note, I take your post to be reading values from 3 different lines, e.g.:
%s
%s
%d %d
(primarily evidenced by your use of fgets, a line-oriented input function, which reads a line of input (up to and including the '\n') each time it is called.) If that is not the case, then the following does not apply (and can be greatly simplified)
Since you are reading multiple values into a single element in an array of struct, you may find it better (and more robust), to read each value and validate each value using temporary values before you start copying information into your structure members themselves. This allows you to (1) validate the read of all values, and (2) validate the parse, or conversion, of all required values before storing members in your struct and incrementing your array index.
Additionally, you will need to remove the tailing '\n' from both title and artist to prevent having embedded newlines dangling off the end of your strings (which will cause havoc with searching for either a title or artist). For instance, putting it all together, you could do something like:
void rmlf (char *s);
....
char title[MAX_TITLE] = "";
char artist[MAX_ARTIST = "";
char a[10] = "";
int min, sec;
...
while (fgets (title, MAX_TITLE, file) && /* validate read of values */
fgets (artist, MAX_ARTIST, file) &&
fgets (a, 10, file)) {
if (sscanf (a, "%d %d", &min, &sec) != 2) { /* validate conversion */
fprintf (stderr, "error: failed to parse 'min' 'sec'.\n");
continue; /* skip line - tailor to your needs */
}
rmlf (title); /* remove trailing newline */
rmlf (artist);
s[i].time.min = min; /* copy to struct members & increment index */
s[i].time.sec = sec;
strncpy (s[i].title, title, MAX_TITLE);
strncpy (s[i++].artist, artist, MAX_ARTIST);
}
/** remove tailing newline from 's'. */
void rmlf (char *s)
{
if (!s || !*s) return;
for (; *s && *s != '\n'; s++) {}
*s = 0;
}
(note: this will also read all values until an EOF is encountered without using feof (see Related link: Why is “while ( !feof (file) )” always wrong?))
Protecting Against a Short-Read with fgets
Following on from Jonathan's comment, when using fgets you should really check to insure you have actually read the entire line, and not experienced a short read where the maximum character value you supply is not sufficient to read the entire line (e.g. a short read because characters in that line remain unread)
If a short read occurs, that will completely destroy your ability to read any further lines from the file, unless you handle the failure correctly. This is because the next attempt to read will NOT start reading on the line you think it is reading and instead attempt to read the remaining characters of the line where the short read occurred.
You can validate a read by fgets by validating the last character read into your buffer is in fact a '\n' character. (if the line is longer than the max you specify, the last character before the nul-terminating character will be an ordinary character instead.) If a short read is encountered, you must then read and discard the remaining characters in the long line before continuing with your next read. (unless you are using a dynamically allocated buffer where you can simply realloc as required to read the remainder of the line, and your data structure)
Your situation complicates the validation by requiring data from 3 lines from the input file for each struct element. You must always maintain your 3-line read in sync reading all 3 lines as a group during each iteration of your read loop (even if a short read occurs). That means you must validate that all 3 lines were read and that no short read occurred in order to handle any one short read without exiting your input loop. (you can validate each individually if you just want to terminate input on any one short read, but that leads to a very inflexible input routine.
You can tweak the rmlf function above to a function that validates each read by fgets in addition to removing the trailing newline from the input. I have done that below in a function called, surprisingly, shortread. The tweaks to the original function and read loop could be coded something like this:
int shortread (char *s, FILE *fp);
...
for (idx = 0; idx < MAX_SONGS;) {
int t, a, b;
t = a = b = 0;
/* validate fgets read of complete line */
if (!fgets (title, MAX_TITLE, fp)) break;
t = shortread (title, fp);
if (!fgets (artist, MAX_ARTIST, fp)) break;
a = shortread (artist, fp);
if (!fgets (buf, MAX_MINSEC, fp)) break;
b = shortread (buf, fp);
if (t || a || b) continue; /* if any shortread, skip */
if (sscanf (buf, "%d %d", &min, &sec) != 2) { /* validate conversion */
fprintf (stderr, "error: failed to parse 'min' 'sec'.\n");
continue; /* skip line - tailor to your needs */
}
s[idx].time.min = min; /* copy to struct members & increment index */
s[idx].time.sec = sec;
strncpy (s[idx].title, title, MAX_TITLE);
strncpy (s[idx].artist, artist, MAX_ARTIST);
idx++;
}
...
/** validate complete line read, remove tailing newline from 's'.
* returns 1 on shortread, 0 - valid read, -1 invalid/empty string.
* if shortread, read/discard remainder of long line.
*/
int shortread (char *s, FILE *fp)
{
if (!s || !*s) return -1;
for (; *s && *s != '\n'; s++) {}
if (*s != '\n') {
int c;
while ((c = fgetc (fp)) != '\n' && c != EOF) {}
return 1;
}
*s = 0;
return 0;
}
(note: in the example above the result of the shortread check for each of the lines that make up and title, artist, time group.)
To validate the approach I put together a short example that will help put it all in context. Look over the example and let me know if you have any further questions.
#include <stdio.h>
#include <string.h>
/* constant definitions */
enum { MAX_MINSEC = 10, MAX_ARTIST = 32, MAX_TITLE = 48, MAX_SONGS = 64 };
typedef struct {
int min;
int sec;
} stime;
typedef struct {
char title[MAX_TITLE];
char artist[MAX_ARTIST];
stime time;
} songs;
int shortread (char *s, FILE *fp);
int main (int argc, char **argv) {
char title[MAX_TITLE] = "";
char artist[MAX_ARTIST] = "";
char buf[MAX_MINSEC] = "";
int i, idx, min, sec;
songs s[MAX_SONGS] = {{ .title = "", .artist = "" }};
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
for (idx = 0; idx < MAX_SONGS;) {
int t, a, b;
t = a = b = 0;
/* validate fgets read of complete line */
if (!fgets (title, MAX_TITLE, fp)) break;
t = shortread (title, fp);
if (!fgets (artist, MAX_ARTIST, fp)) break;
a = shortread (artist, fp);
if (!fgets (buf, MAX_MINSEC, fp)) break;
b = shortread (buf, fp);
if (t || a || b) continue; /* if any shortread, skip */
if (sscanf (buf, "%d %d", &min, &sec) != 2) { /* validate conversion */
fprintf (stderr, "error: failed to parse 'min' 'sec'.\n");
continue; /* skip line - tailor to your needs */
}
s[idx].time.min = min; /* copy to struct members & increment index */
s[idx].time.sec = sec;
strncpy (s[idx].title, title, MAX_TITLE);
strncpy (s[idx].artist, artist, MAX_ARTIST);
idx++;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (i = 0; i < idx; i++)
printf (" %2d:%2d %-32s %s\n", s[i].time.min, s[i].time.sec,
s[i].artist, s[i].title);
return 0;
}
/** validate complete line read, remove tailing newline from 's'.
* returns 1 on shortread, 0 - valid read, -1 invalid/empty string.
* if shortread, read/discard remainder of long line.
*/
int shortread (char *s, FILE *fp)
{
if (!s || !*s) return -1;
for (; *s && *s != '\n'; s++) {}
if (*s != '\n') {
int c;
while ((c = fgetc (fp)) != '\n' && c != EOF) {}
return 1;
}
*s = 0;
return 0;
}
Example Input
$ cat ../dat/titleartist.txt
First Title I Like
First Artist I Like
3 40
Second Title That Is Way Way Too Long To Fit In MAX_TITLE Characters
Second Artist is Fine
12 43
Third Title is Fine
Third Artist is Way Way Too Long To Fit in MAX_ARTIST
3 23
Fourth Title is Good
Fourth Artist is Good
32274 558212 (too long for MAX_MINSEC)
Fifth Title is Good
Fifth Artist is Good
4 27
Example Use/Output
$ ./bin/titleartist <../dat/titleartist.txt
3:40 First Artist I Like First Title I Like
4:27 Fifth Artist is Good Fifth Title is Good
Instead of sscanf(), I would use strtok() and atoi().
Just curious, why only 10 bytes for the two integers? Are you sure they are always that small?
By the way, I apologize for such a short answer. I'm sure there is a way to get sscanf() to work for you, but in my experience sscanf() can be rather finicky so I'm not a big fan. When parsing input with C, I have just found it a lot more efficient (in terms of how long it takes to write and debug the code) to just tokenize the input with strtok() and convert each piece individually with the various ato? functions (atoi, atof, atol, strtod, etc.; see stdlib.h). It keeps things simpler, because each piece of input is handled individually, which makes debugging any problems (should they arise) much easier. In the end I typically spend a lot less time getting such code to work reliably than I did when I used to try to use sscanf().
Use "%*s %*s %d %d" as your format string, instead...
You seem to be expecting sscanf to automagically skip the two tokens leading up to the decimal digit fields. It doesn't do that unless you explicitly tell it to (hence the pair of %*s).
You can't expect the people who designed C to have designed it the same way as you would. You NEED to check the return value, as iharob said.
That's not all. You NEED to read (and understand reelatively well) the entire scanf manual (the one written by OpenGroup is okay). That way you know how to use the function (including all of the subtle nuances of format strings) and what to do with the return vale.
As a programmer, you need to read. Remember that well.

C How to ignore empty lines in user input?

here is my current code:
int num = 0;
char c = '#';
scanf("%d",&num);
do{
for (int i=0;i<num;i++){
printf("%c",c);
}
printf("\n");
}
while (scanf("%d", &num) == 1);
How would I have it so that if the user doesn't enter anything, that the program won't spit out a newline?
Any help is appreciated, thank you!
This code should work for what you want to do :
#include <stdio.h>
int main()
{
int num = 0;
char c = '#';
char readLine[50];
while ((fgets(readLine, sizeof readLine, stdin) != NULL) && sscanf(readLine, "%d", &num) == 1)
{
for (int i=0;i<num;i++){
printf("%c",c);
}
printf("\n");
fflush(stdout);
}
return 0;
}
The behaviour of this code is the following : fgets will read anything you enter in the standard stream (stdin), and put it in the readLine array. The program will then try to read the number which is in your readLine variable and put it in your num variable with the sscanf function. If a number is read, the program will execute the behaviour you did present in your question (writing a # character "num" times), and go back to the beginning of the loop. If anything else than a number has been read, the loop is stopped.
In general, avoid scanf. It's very easy to leave yourself with unprocessed cruft on the input stream. Instead, read the whole line and then use sscanf (or something else) to process it. This guarantees that you won't get stuck with a partially read line, those are hard to debug.
I prefer getline to fgets to read lines. fgets requires you to guess how long the input might be, and input might get truncated. getline will allocate the memory to read the line for you avoiding buffer overflow or truncation problems.
NOTE: getline is it's not a C standard function, but a POSIX one and fairly recent (2008), though it was a GNU extension well before that. Some older compilers may not have it.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char c = '#';
char *line = NULL;
size_t linelen = 0;
/* First read the whole line */
while( getline(&line, &linelen, stdin) > 0 ) {
/* Then figure out what's in it */
long num = 0;
if( sscanf(line, "%ld", &num) > 0 ) {
for( int i = 0; i < num; i++ ) {
printf("%c", c);
}
printf("\n");
}
}
free(line);
return 0;
}
if( sscanf(line, "%ld", &num) > 0 ) { will ignore any line that does not match any part of the pattern, such as a blank line or a line full of words, by checking how many things matched. Yet it will still handle 0 as a valid input.
$ ./test
foo
bar
foo123
12
############
1
#
0
2
##
I also moved num inside the loop to guarantee it's reinitialized each iteration, and on the general principle of putting your variables in minimum scopes to avoid interference. And I upgraded it to a long int better able to handle the unpredictably large numbers users might type in.
Here is how I have done input parsing over the years using the fgets() and sscanf() functions. I don't write c++ much, and if I can I keep code within old style ansi C then I do.
The fgets and sscanf functions from the stdio.h library are universal and are always available on any platform.
For a character array used to read in anything, I generally set LINE_SIZE to 256 or 512 even if I know typically the line to be read is 80 characters or less. With any computer today having over 1GB of RAM, not worth worrying about allocating an extra 500 or so bytes. Obviously, if you have no idea how long the input line is then you either have to:
guess at what LINE_SIZE should be set to and not worry about it
or verify a newline character is present in line[] prior to a null character after calling fgets().
# include <stdio.h>
# define LINE_SIZE 256
int main ( int argc, char *argv[] )
{
FILE *fp;
char line[LINE_SIZE];
int nn;
int value;
fp = fopen( "somefile", "r" );
fgets( line, LINE_SIZE, fp );
/*
this way to read from standard input (i.e. the keyboard)
using fgets with stdin prevents compiler warning when using
deprecated gets function
fgets( line, LINE_SIZE, stdin );
*/
if ( line[0] != '\n' )
{
/* definitely not a blank line */
nn = sscanf( line, "%d", &num );
if ( nn == 1 )
{
/* some number placed into num variable that met the
%d conversion for the sscanf function
*/
}
}
return 0;

Using fgets() to read multiple lines. How to go to the Next line?

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
number of decks
num of players (4)
num of cards (13)
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);
}
}
}
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 no [mc]alloc()'s 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
#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++]);
}
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
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

Resources