bug when reading integers from a text file in C - c

I am a little bit desperate as I wasted my last 4 hours looking for a solution for a simple/stupid thing. I have a project in school in which I must read integers from a text file and then calculate the maximum of them. The thing is that these numbers are not necessarily separated by spaces, but also tabs ( \t ) or newlines (\n). I use the fscanf function to read my integers, but the problem is that after the last number I have a space in my file, so it reads 2 times the last number(I do not know why). Normally one would say "Just delete that space", but my teacher is going to test the program in several manners, and he warned us that the program must be robust (we have to be able to manage the spaces, \t, \n so we can read correctly the numbers, so this is why he left on purpose a space after the last number). Here is my code with the sample text:
FILE* file = NULL;
int *t = NULL, *new_size = NULL;
int temp, count;
file = fopen(argv[1],"r");
do
{
fscanf(file,"%d",&temp);
count++;
new_size = (int*) realloc (t, count * sizeof(int));
if (new_size != NULL)
{
t = new_size;
t[count-1] = temp;
}
else
{
free(t);
puts("Erreur d\'allocation memoire!\n");
exit(1);
}
} while(!feof(file));
fclose(file);
printf ("Numbers read\n:");
for(i = 0; i < count; i++)
{
printf ("%d ", t[i]);
}
And my argument is a file named data.txt which contains: 3 1 7 0 4 9 6 150 and a \n at the end. So basically, my program reads the first 8 integers, and after that, since it is not EndOfFile, it still reads the last one (150) again. So my output is :
Numbers read:
3 1 7 0 4 9 6 150 150
Could anyone tell me what I should do in order to make my program more robust? I need to manage the same type os error if there is a space at the end, or even a tab (\t).
Any help would be appreciated!

Check the return value of fscanf
if (fscanf(file,"%d",&temp) != 1) break;
and break out of the loop if it doesn't find a number anymore. fscanf returns the number of successful conversions, and that should be used for error checking and handling.

Use the result of fscanf(), which returns the number of assignments made, as the terminating condition of the loop. After the loop check that EOF was reached and the loop did not terminate due to some other failure.

If the numbers are separated by a space, then you can read a space with fscanf and then an integer.To make the program robust, always see if fscanf fails.If it fails then stop reading integers, else continue.
bool good=true;
while(good)
{
char separator;
if(fscanf(file,"%c",&separator)==1)
{
if(separator!=' ' && separator!='\t' && separator!='\n')
{
fprintf(stderr,"Illegal character found in file");
// You can choose if break the loop setting good to false, or
// if to continue, this depends on your assignment
}
if(fscanf(file,"%d",&temp)==1)
{
< Push temp to your list/array >
}
else
{
good=false;
}
}
else
{
good=false;
}
}

Related

fgets storing unknown data from text file

I was creating a word guessing game that reads a text file line per line until it finds a random word and stores it in a string (word). Then the user enters letters until all the letters of the stored word are revealed.
So far it works perfectly but every time it's the very first word that is read some unknown characters get stored at the beginning of char word[20].
Note that I use C mobile app and it uses clang 6.0 compiler I think. So does the error come from my code or it come from their app? (which I love).
Here is the FULL, clearer code:
//guess the right word
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define NB_OF_WORDS 3 //number of words in text file
main() {
char begin, word[20] = { 0 }, guessedletter;
int num, rightletter, success;
int i = 0;
int show[20] = { 0 };//shown letters
FILE *ressource = NULL;
srand(time(NULL));
if ((ressource = fopen("ressource.txt", "r")) == NULL) {
fprintf(stderr, "Error ressource.txt");//open in read only mode
exit(EXIT_FAILURE);
}
printf("Welcome to Word Guess, a word guessing contest.\n"
"You have to find the letters of a word and guess what word it is.\n"
"Begin? (y/n)\n");
while ((begin = getchar()) == 'y') { //game loop
/*reinitializations*/
fseek(ressource, 3, SEEK_SET);//replaces rewind(ressource)
success = FALSE;
rightletter = 0;
num = 0;
num = rand() % NB_OF_WORDS; //random number between 0 and NB-1
for (i = 0; i < 20; i++) {
show[i] = FALSE;
word[i] = 0;
}
i = 0;
/*end of reinitializations*/
while (i <= num) {//reads line by line until random word is stored
if (fgets(word, 20, ressource) == NULL) {
fprintf(stderr, "fgets did not work");
exit(EXIT_FAILURE);
}
i++;
}
printf("%s", word);//here is just for testing if the read word is well read. Which isn't the case if num=0
for (i = 0; i < 20; i++)
if (word[i] == '\n')
word[i] = '\0';//adds zero character to show where the string ends
while (!success) { //guessing loop
printf("\nWrite a letter: ");
scanf("%c", &guessedletter);
printf("\n");
for (i = 0; word[i] != '\0'; i++) { //compares entered letter to letter from string. If they match...
if (word[i] == guessedletter) {
if (!show[i])
rightletter++;//avoids letters entered twice from increasing count
show[i] = TRUE;
}
if (show[i])
printf("%c", word[i]);//...a letter is revealed...
else
printf("*");//else it stays hidden
}
if (rightletter == strlen(word))
success=TRUE;//if all the right letters found (same number of letters found as number of letters in the words) you win
}
printf("\nCongratulations you have won!\nDo you want to replay? (y/n)");
getchar();//clears newline character
}
fclose(ressource);
return 0;
}
When num=0, I get a weird sign before the printed word as if the first characters of the text file were not supposed to be there...
Moreover during the guessing game if the word to guess (word[20]) is "annoying" let's say and that it's the first word from "ressource.txt" (num=0). The word will print like so on the screen once I guessed all the letters: ***annoying. Which does not happen with ANY other words from the list.
I'm new to this site and post from my phone... sorry for any mistakes.
EDIT: removed fgetc for fgets. Still get several unknown characters if fgets reads first line.
EDIT 2: added the whole code, translated mot[20] into word[20], added error testing
EDIT 3: Replacing rewind(ressource); by fseek(ressource, 3, SEEK_SET); solved the problem. Which means there are indeed three unknown characters at the beginning of the text file
There are multiple problems in the posted code:
You only posted a fragment of code, the rest of the function could cause problems that cannot be analysed from what you posted. Please post the complete code to a minimal program exhibiting the problem.
You do not test for end of file in the while loop. If you try to skip more lines than are present in the file, this loop will run indefinitely.
You do not test the return value of fgets(): if by chance you skipped the full contents of the file in the preceding while loop, fgets() will fail and return NULL, leaving mot in an undetermined state, causing unexpected behavior in the subsequent printf.
EDIT: the modified code still does not check the return value of fgets().
EDIT: thank you for posting the full code, but modifying the question this way makes this answer and the comments irrelevant.
Your dictionary file ressource.txt seems to start with a BOM (Byte Order Mark) encoded in UTF-8. I am guessing it contains French words including some with accented letters such as reculées and encodées... Your text editor saved it encoded in UTF-8 with an extra code-point at the beginning for other programs to determine this encoding easily. The problem with that is your program does not handle UTF-8. It assumes each byte read from stdin represents a single character. It might by chance match some words with accented letters, but will more likely fail to find them.

Putting users input consisting of numbers separated by commas into an array?

I am taking in 10 numbers from the user (user enters them at a prompt, and the numbers are separated by commas, as so: 245645, -243, 4245). How can I put these elements into an array? As shown below, I have used scanf which does not work as I had hoped. Any suggestions would be appreciated.
//User will pass ten numbers, separated by commas. This is to be put into an array.
#include <stdio.h>
int main ()
{
int A[10]; // array to contain in users input.
printf("Enter your numbers: ");
scanf("%d", &A[10]);
return 0;
}
You have to consume the comma as well in scanf:
for(int i=0; i<10; i++) { /* for each element in A */
if(0==scanf("%d,", &A[i])) { /* read a number from stdin into A[i] and then consume a commma (if any) */
break; /* if no digit was read, user entered less than 10 numbers separated by commas */
/* alternatively error handling if fewer than 10 is illegal */
}
}
I won't write the whole thing for you.
But I can definitely help.
One of the ways to do that will be:
Get a string that contains 10 comma-separated numbers: fgets() may be?
Validate the string, trim white-spaces as well, makes life easier
Pick out a number from string: strtol() may be?
Search for a ',' character in the string, and set pointer to the next index after ',': strchr() may be?
Repeat steps 3 and 4 for a total of 10 times (from here, 9 times actually)
Print the numbers
The code below would do half of your job. The only remaining part would be to get string from user and validate it.
The intention to have a string declared and initialised upfront is to put more emphasise on actual parsing of data which appear complicated to beginners (no offence).
Before we look at the code below, lets read a few things first.
You might want to take a look at the man page for strtol() function
You might want to take a look at the man page for fgets() function, which is not used in the code below, but you may end-up using it to achieve step 1.
I already concede the fact that this may not be the best way to achieve it, and I would happily agree that this code below can be made better in thousand ways by adding various error check, but I leave that to you to explore and implement.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
int i = 0, j = 0;
char *Str = "1,11,21,1211,111221,312211,1234,4321,123,789";
char *ptr;
long ret;
long array[10] = { [0 ... 9] = 0 };
// So, lets assume step 1 and 2 are taken care of.
// Iterate though the data for 10 times
for(i = 0; i < 10; i++)
{
ret = strtol(Str, &ptr, 10);
// Check if its a number
if(ret != 0)
{
// Its is a number!
// Put it in the array
array[j] = ret;
// Increment the index so that next number will not over-write the existing one
j++;
// Find the next ',' in the string
Str = strchr(Str, ',');
if(Str == NULL)
{
// Handle this condition that there are no more commas in the string!
break;
}
// Assuming that the ',' is found, increment the pointer to index next to ','
Str++;
}
}
// Print the array
for(i = 0; i < j; i++)
printf("%ld\n", array[i]);
}
This prints the following output:
1
11
21
1211
111221
312211
1234
4321
123
789
Hope I have got you started, Good luck.

How to read into array of strings using gets?

I have an array of 15 strings (which don't all have to necessarily be used), and despite reading everywhere that gets should never be used, for some reason I believe it is most convenient for me for this program.
After prompting the user to specify how many rows and columns he wants, to create a matrix, I ask him to enter the matrix values, one row per line at a time. I do this using gets. Simultaneously, I want to scan through the string for the amount of spaces entered to ensure that the user is entering the appropriate amount of numbers that correspond to the amount of columns specified.
At the end I want to print out the second row that I entered.
You can assume rowone and colone are already defined, I just didn't copy that part of the code to save space.
int i=0, rowone, colone, sbar=0, inputs=0;
char matrixone[15][10000];
......
printf("input your matrix\n");
for (i=0;i<rowone;i++){
gets(matrixone[i]);
while(matrixone[i][inputs]!='\n'){
if (mplier[i][inputs] == ' '){
sbar++;
inputs++;
}
else
inputs++;
}
if (sbar>=colone||sbar<colone-1){
printf("Too many/too few inputs per line\n");
main();
}
sbar = 0;
inputs = 0;
}
puts(matrixone[2])
I get warnings when compiling and ultimately not even the chance to input the matrix as "Too many/too few inputs" always pops up.
Any help would be greatly appreciated, thank you!
Infinite loop.
Or more properly, eventually matrixone[i][inputs] accesses way beyond what it was read and maybe beyond the array itself. This leads to undefined behavior (UB).
gets() consumes, but does not save '\n'.
gets(matrixone[i]);
while(matrixone[i][inputs]!='\n'){ // why should this quit?
...
inputs++;
}
Instead, drop gets() as it is dropped from the language for 5 years now, use fgets(), check return value and look for the end of the string as well
if (fgets(matrixone[i], sizeof matrixone[i], stdin) {
while(matrixone[i][inputs] != '\0'){
...
}
}
Suggestion for OP (guessing OP's goal)
char *matrixone[15] = { 0 };
printf("input your matrix\n");
for (i=0;i<min(rowone,15);i++){
buf[1000];
if (fgets( buf, sizeof buf, stdin) == NULL) break;
buf[strcspn(buf, "\n")] = 0;
// qualify buf concerning ' ' , sbar, etc
...
matrixone[i] = strdup(buf);
}

Check if user input into an array is too long?

I am getting the user to input 4 numbers. They can be input: 1 2 3 4 or 1234 or 1 2 34 , etc. I am currently using
int array[4];
scanf("%1x%1x%1x%1x", &array[0], &array[1], &array[2], &array[3]);
However, I want to display an error if the user inputs too many numbers: 12345 or 1 2 3 4 5 or 1 2 345 , etc.
How can I do this?
I am very new to C, so please explain as much as possible.
//
Thanks for your help.
What I have now tried to do is:
char line[101];
printf("Please input);
fgets(line, 101, stdin);
if (strlen(line)>5)
{
printf("Input is too large");
}
else
{
array[0]=line[0]-'0'; array[1]=line[1]-'0'; array[2]=line[2]-'0'; array[3]=line[3]-'0';
printf("%d%d%d%d", array[0], array[1], array[2], array[3]);
}
Is this a sensible and acceptable way? It compiles and appears to work on Visual Studios. Will it compile and run on C?
OP is on the right track, but needs adjust to deal with errors.
The current approach, using scanf() can be used to detect problems, but not well recover. Instead, use a fgets()/sscanf() combination.
char line[101];
if (fgets(line, sizeof line, stdin) == NULL) HandleEOForIOError();
unsigned arr[4];
int ch;
int cnt = sscanf(line, "%1x%1x%1x%1x %c", &arr[0], &arr[1], &arr[2],&arr[3],&ch);
if (cnt == 4) JustRight();
if (cnt < 4) Handle_TooFew();
if (cnt > 4) Handle_TooMany(); // cnt == 5
ch catches any lurking non-whitespace char after the 4 numbers.
Use %1u if looking for 1 decimal digit into an unsigned.
Use %1d if looking for 1 decimal digit into an int.
OP 2nd approach array[0]=line[0]-'0'; ..., is not bad, but has some shortcomings. It does not perform good error checking (non-numeric) nor handles hexadecimal numbers like the first. Further, it does not allow for leading or interspersed spaces.
Your question might be operating system specific. I am assuming it could be Linux.
You could first read an entire line with getline(3) (or readline(3), or even fgets(3) if you accept to set an upper limit to your input line size) then parse that line (e.g. with sscanf(3) and use the %n format specifier). Don't forget to test the result of sscanf (the number of read items).
So perhaps something like
int a=0,b=0,c=0,d=0;
char* line=NULL;
size_t linesize=0;
int lastpos= -1;
ssize_t linelen=getline(&line,&linesize,stdin);
if (linelen<0) { perror("getline"); exit(EXIT_FAILURE); };
int nbscanned=sscanf(line," %1d%1d%1d%1d %n", &a,&b,&c,&d,&lastpos);
if (nbscanned>=4 && lastpos==linelen) {
// be happy
do_something_with(a,b,c,d);
}
else {
// be unhappy
fprintf(stderr, "wrong input line %s\n", line);
exit(EXIT_FAILURE);
}
free(line); line=NULL;
And once you have the entire line, you could parse it by other means like successive calls of strtol(3).
Then, the issue is what happens if the stdin has more than one line. I cannot guess what you want in that case. Maybe feof(3) is relevant.
I believe that my solution might not be Linux specific, but I don't know. It probably should work on Posix 2008 compliant operating systems.
Be careful about the result of sscanf when having a %n conversion specification. The man page tells that standards might be contradictory on that corner case.
If your operating system is not Posix compliant (e.g. Windows) then you should find another way. If you accept to limit line size to e.g. 128 you might code
char line[128];
memset (line, 0, sizeof(line));
fgets(line, sizeof(line), stdin);
ssize_t linelen = strlen(line);
then you do append the sscanf and following code from the previous (i.e. first) code chunk (but without the last line calling free(line)).
What you are trying to get is 4 digits with or without spaces between them. For that, you can take a string as input and then check that string character by character and count the number of digits(and spaces and other characters) in the string and perform the desired action/ display the required message.
You can't do that with scanf. Problem is, there are ways to make scanf search for something after the 4 numbers, but all of them will just sit there and wait for more user input if the user does NOT enter more. So you'd need to use gets() or fgets() and parse the string to do that.
It would probably be easier for you to change your program, so that you ask for one number at a time - then you ask 4 times, and you're done with it, so something along these lines, in pseudo code:
i = 0
while i < 4
ask for number
scanf number and save in array at index i
E.g
#include <stdio.h>
int main(void){
int array[4], ch;
size_t i, size = sizeof(array)/sizeof(*array);//4
i = 0;
while(i < size){
if(1!=scanf("%1x", &array[i])){
//printf("invalid input");
scanf("%*[^0123456789abcdefABCDEF]");//or "%*[^0-9A-Fa-f]"
} else {
++i;
}
}
if('\n' != (ch = getchar())){
printf("Extra input !\n");
scanf("%*[^\n]");//remove extra input
}
for(i=0;i<size;++i){
printf("%x", array[i]);
}
printf("\n");
return 0;
}

Read in Numbers from a File in C

I have a file called points.dat which reads something like:
5
2 5
-1 18
0 6
1 -1
10 0
The first number is how many ordered pairs there are. The next 5 lines contain those ordered pairs. What can I do to read in the first number, determine how many points there are (from here I can malloc an array of structs to store the points in).
My problem is that fgetc doesn't really do the job here. What if the first number is two digits? Say the first number is 10. fgetc will only retrieve the '1'. Also, fgets doesn't really work, since you need to supply it the length of the amount of characters you want to read in. The same applies for fscanf.
The real trouble comes in when it's time to retrieve the ordered pairs. I have no idea how to do this either. My only thoughts so far is look at a line: if it sees non-spaces or non-'\n's, it will read in the number as the x coordinate of point 1. Loop. Get y coordinate. Once it hits a '\n', it will now move on to the next line, and begin looking for values to store in the next struct in the array of structs.
(While doing this, I also need to be sure atoi can convert all of these into integers... ).
If anyone has any ideas to help, they are appreciated.
For the first line use int numValuesRead = fscanf(file, "%d", &totnums);
Then, use numValuesRead = fscanf(file, "%d %d", &num1, &num2); to read the other lines.
fscanf returns the number of value read. You should always check it.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int x, y;
} Point;
int main ()
{
int numOf;
Point *myPoints = NULL;
FILE *myfile = fopen ("myfile.txt","r");
if (myfile == NULL)
perror ("Error opening file"); //or return 1;
else
{
fscanf(myfile, "%d", &numOf);
myPoints = (Point *)malloc(sizeof(Point) * numOf);
while ( !feof (myfile) && numOf-- )
{
fscanf(myfile, "%d %d", &(myPoints[numOf].x), &(myPoints[numOf].y));
}
}
fclose(myfile);
//Do stuff with array
free ((void *)myPoints);
getchar();//Press enter to close debugger etc.
return 0;
}
Sorry for the delay.

Resources