Link to home
Start Free TrialLog in
Avatar of l4zarus
l4zarusFlag for United States of America

asked on

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

Avatar of SordSord
SordSord
Flag of United States of America image

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.
Avatar of l4zarus

ASKER

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.
ASKER CERTIFIED SOLUTION
Avatar of l4zarus
l4zarus
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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);