• C

Why would fgets skip data?

I'm working on this code to read in an array of integers from a file, and I have it working for one test file, but I added another line to the array and for some reason when it has to read this extra line, fgets() seemingly "skips" over a few characters.

Attached is the function. This is the output I've been getting (the last line of the file, no different than any other line, has the numbers 5, 2, and 1. As shown by the sample output, doing a manual scan of the char *line that I read in shows that it somehow read in the 1 and a \n but "skipped" the first few characters...

Rows: 4
Cols: 3
Allocating 48 bytes for array...
'12     5       7
¿'
'12     5       7
'
'12'    Putting 12 in [(0 * 3 + 0) *4] = [0]
'5'     Putting 5 in [(0 * 3 + 1) *4] = [4]
'7'     Putting 7 in [(0 * 3 + 2) *4] = [8]
'8      16      2
¿'
'8      16      2
'
'8'     Putting 8 in [(1 * 3 + 0) *4] = [12]
'16'    Putting 16 in [(1 * 3 + 1) *4] = [16]
'2'     Putting 2 in [(1 * 3 + 2) *4] = [20]
'3      9       18
¿'
'3      9       18
'
'3'     Putting 3 in [(2 * 3 + 0) *4] = [24]
'9'     Putting 9 in [(2 * 3 + 1) *4] = [28]
'18'    Putting 18 in [(2 * 3 + 2) *4] = [32]
'       1
¿'
''
Rows: 4
Cols: 3

/*
getArrayFromFile
-----------------------
char *filename - pointer to the name of the matrix file to open.
int *rRows - pointer to allocated space to store the number of rows
int *rCols - pointer to allocated space to store the number of columns
*/
int *getArrayFromFile(char *filename, int *rRows, int *rCols)
{
	int *rows = malloc(sizeof(int));
	int *cols = malloc(sizeof(int));
	
	getFileInformation(filename, rows, cols);
	
	printf("Rows: %d\n", *rows);		//DEBUG
	printf("Cols: %d\n", *cols);		//DEBUG
	
	int bytesToAllocate = ((*rows)*(*cols)*sizeof(int));
	printf("Allocating %d bytes for array...\n",bytesToAllocate);
	
	int *A = malloc(bytesToAllocate*2);
	// Not sure why I'm multiplying it by 2... it allocated half of what I wanted.
	
	// ================ FILL ARRAY WITH NUMBERS ================
	
	FILE *file;
	file = fopen(filename, "r");
	if(file == NULL)
	{
		fprintf(stderr,"File could not be opened.");
		exit(1);
	}
	
	char *line = malloc(sizeof(char)*255);
	int col = 0, row = 0;
	
	//fgets(line,255,file);
	//while(!feof(file))
	while(fgets(line,255,file) != NULL)
	{
		int q = 0;
		printf("'");
		for(q = 0; q < 255; q++)
		{
			printf("%c",line[q]);
		}
		printf("'\n");
		
		printf("'%s'\n",line);		//DEBUG
		int i = 0;
		while(line[i] != '\n' && line[i] != '\0')
		{
			// Skip white space until we hit a number.
			while(line[i] == ' ' || line[i] == '\t')
			{
				i++;
			}
			
			// Reset number collector
			int numCount = 0;
			char num[10] = {'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'};
			
			// Get number until we hit white space again.
			printf("'");	//DEBUG
			while(line[i] != ' ' && line[i] != '\t' && line[i] != '\n' && line != '\0')
			{
				printf("%c",line[i]);	//DEBUG
				num[numCount] = line[i];
				numCount++;
				i++;
			}
			printf("'\t");	//DEBUG
			
			int positionToPlace = (row * (*cols) + col)*sizeof(int);
			printf("Putting %d in [(%d * %d + %d) *%d] = [%d]\n",atoi(num),row,*cols,col,sizeof(int),positionToPlace);
			
			// We have number, put it in array
			A[positionToPlace] = atoi(num);

			col++;
			i++;
		}
		row++;
		col = 0; // Reset col in preparation for next row.
		
		//fgets(line,255,file);
	}
	
	free(line);
	fclose(file);
	
	// =========================================================
	
	*rRows = *rows;
	*rCols = *cols;
	
	free(rows);
	free(cols);
	
	return A;
}

Open in new window

l4zarusAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

SordSordCommented:
Your manual scan of *line is not valid. The fgets function will put some number of characters in the memory pointed at by line, with the last character being a 0 value (null terminated). Your manual scan continues past that point and will show you characters that were not necessarily read by the fgets function.

The "printf("'%s'\n",line);" debug line indicates that you are not reading the last line at all. One possible reason might be that you have an end-of-file marker in the file and the last line is after that marker. That would cause fgets to stop reading before it got to your last line. A simple way to test it would be to create a new file and enter all four lines in at once to see how it changes the output.
0
l4zarusAuthor Commented:
By testing and messing around with things I've figured out that the problem comes down to
this line:
                      int positionToPlace = (row * (*cols) + col)*sizeof(int);
                       A[positionToPlace] = atoi(num);

Everything outputs correctly when I comment this line out. With it in, it skips or has skewed results with the last line. How does placing the number (no relation to *line) in to the array (no relation to *line) do anything to screw with.. *line.
0
l4zarusAuthor Commented:
Whole problem was caused by the *sizeof(int) - I was writing data ahead of the allocated array and probably into the line reads.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
SordSordCommented:
It is a memory overwrite (probably writing over the FILE structure.  At the beginning you malloc some memory and assign it to and int pointer (in particular, you allocate 96 bytes of memory).

int *A = malloc(bytesToAllocate*2);

When you get to the "18'    Putting 18 in [(2 * 3 + 2) *4] = [32]" step, positionToPlace is 32, which would be fine if you were accessing the 32 byte in the buffer, but since A is an *int, when you reference A[32], it is the 32nd int value (or 32*4 = 128th byte).

You can fix the problem by not multiplying by the sizeof(int) since that will automatically happen with the A[] reference.

So change:
int positionToPlace = (row * (*cols) + col)*sizeof(int);
to
int positionToPlace = (row * (*cols) + col);
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.