Reputation: 13
I have a text file in which there are only numbers (0, 1, 2 & 3) and I want to process the data to know how many times each number appears.
The following program works with small text file (<100 numbers) but with bigger files (I need to process in the end several thousands of data) the program reads numbers that are not in the text file.
Here's my code :
FILE *file;
char c;
int nb;
int th0 = 0, th1 = 0, th2 = 0, th3 = 0;
file = fopen("../data", "r");
if (file == NULL) {
printf("ERROR FILE: %s", strerror(errno));
return EXIT_FAILURE;
}
while (1) {
if (fscanf(file, "%c", &c) == EOF)
break;
nb = atoi(&c);
printf("%d", nb);
switch (nb) {
case 0:
th0++;
break;
case 1:
th1++;
break;
case 2:
th2++;
break;
case 3:
th3++;
break;
default:
break;
}
}
I would appreciate any suggestions.
EDIT, the input text with the output :
Data file (181 numbers):
001110120101010102012012021201021202102012012012012010210210210120120230103120130230123201320310231023102302301231203213210131032103210230120320310320213202301320123120312302321023
Output: The end of the reading is not what is in the data file and count 156 numbers
Upvotes: 1
Views: 473
Reputation: 144949
You call atoi
with a pointer to char
that does not point to a proper C string, hence your code has undefined behavior and may seem to work as expected sometimes and misbehave at other times...
Since you are dealing with digits, you could compute the value represented by the digit with a simple subtraction c - '0'
.
You could also simplify the code with an array:
#include <stdio.h>
int main() {
FILE *file;
int c, i;
int count[10] = { 0 };
file = fopen("../data", "r");
if (file == NULL) {
printf("ERROR FILE: %s", strerror(errno));
return EXIT_FAILURE;
}
while ((c = getc(file)) != EOF) {
if (c >= '0' && c <= '9')
count[c - '0']++;
}
fclose(file);
printf("counts:\n");
for (i = 0; i < 10; i++) {
printf("%d: %d\n", i, count[i]);
}
return 0;
}
Upvotes: 0
Reputation: 8142
Your problem is that atoi
expects a string and you're calling it like this:
nb = atoi(&c);
with c
being just a char
. Sometimes that might work, but you're basically hitting undefined behaviour as you can't guarantee that the memory after c
is empty.
Instead you want to calculate nb
differently.
nb = c - '0';
This relies on the fact that in the ASCII table, the numbers 0 to 9 are in a block together. Subtracting the value of '0' from c
will get you the numerical value of that character...assuming it is a digit.
And just to ensure it is a digit you should wrap this if
statement around your code
if(isdigit(c)) // Check if c is a digit
{
nb = c - '0';
switch(nb) {
case 0: th0++;
break;
// rest of switch goes here....
}
}
Upvotes: 4
Reputation: 26753
Looking at
https://en.cppreference.com/w/c/string/byte/atoi
I see
Parameters
str - pointer to the null-terminated byte string to be interpreted
But with
char c;
/* ... */
nb = atoi(&c);
you are using a pointer to a single char
, which is followed by who-knows-what.
For anything which happens to not be a '\0'
you will get a result from atoi()
which is
a) based on access beyond the intended variable
b) for following digits, is a two-digit number or higher
The first option means that your code needs fixing. The second option can explain any number > 9.
Upvotes: 3