find and save an integer from a textfile in C - c

I'm very new to programming and C. I have textfile with some random text and an integer that i want to find and save. The textfile looks something like this (I only want to save 23 in this case, not 56):
# this is a comment
23
this is some random text
56
And this is the code I have:
int *num = malloc(sizeof(int));
*num = fgetc(f);
while(!feof(f)){
*num = fgetc(f);
if(isdigit(*num)){
break;
}
}
if(!isdigit(*num)){
printf("Error: no number found.\n");
free(num);
}
else{
printf("%c\n", *num);
}
I'm kinda stuck right now and my program only prints out the number 2 :/
Very thankful for help.

As #pbn said you're better off using sscanf.
But if you really, really want, you can do it your way, by reading one character at a time, but you'll need to "build" the integer yourself, converting the character to integer, keeping track of what you have, and multiplying by powers of 10 for every digit the number that you already have.
Something like this (not complete code, it's just to get you started):
int c;
int num = 0;
while (c = fgetc(f)) {
if(!isdigit(c)) {
break;
}
num = (num * 10) + (c - '0');
}
The c- '0' part is to convert the text representation of the integer to the integer itself. 0 is character 48, 1 is 49 and so on.
This is assuming that on the line with numbers, you ONLY have numbers, not a mix of numerical and non-numerical characters.
Also, do not use !feof(file).

One option could be using getline and sscanf functions. I assumed that text lines do not contain numbers:
#include <stdio.h>
int main() {
int value, matched = 0;
char *line = NULL;
size_t size;
while(getline(&line, &size, stdin) != -1) {
if ((matched = sscanf(line, "%d", &value)) != 0)
break;
}
if (matched)
printf("value: %d\n", value);
return 0;
}
This part:
while(getline(&line, &size, stdin) != -1) {
will try to read the entire stream line by line.
Next line uses sscanf return value, which is the number of input items successfully matched and assigned, to determine whether the integer value has been found. If so it stops reading the stream.

One simple way in your program is once you find digit don't just stop continue untill you find next " " , "\n" , "\0" . Till then add Number = Number*10 +(*num);, define Number as global or something.

Related

Unexpected result when using atoi() on content of file, read char by char

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
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....
}
}
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.
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;
}

C. Method not reading correctly from textfile when figure contains 2 integers

I have a basic math program where I store the results in a textfile. Each result is stored on a new line in the textfile, and the figures varies between 0 and 15.
The textfile can looking something like this;
1
0
4
9
12
etc.
with each figure on a new line (dont know why the numbers are on the same line here).
The program should find the highest result. It does not matter if there is more than one figure with the same value.
My problem is that, when the figure contains two integers my method only takes the last one and stores this. If I have 15 for example, 5 is stored.
My method looks like this. Would be greatfull for some help!
void BestResult(){
char c;
int max=0;
int converted=0;
if (access("ResultScore.txt", F_OK) != -1){
fPointerScore=fopen("ResultScore.txt", "rt");
while((c=fgetc(fPointerScore))!=EOF){
converted=atoi(&c);
if (converted>max){
max=converted;
}
}
printf("\nThe best result is : %d/15 at the moment", max);
}
AskUserWhatToDo();
}
You are storing it in a character. So char will store only '5' and not '15'. Use data type string or char*.
Problem with your code is, it read all characters(in your file one by one)
Your code need needs to detect end of the line
ch!= '\n'
Try this:
char buffer[10]; /* as big as the biggest number */
char ch;
int i = 0;
double d, dMax = 1000, dMin = -1000;
while ((ch= getc(fp)) != EOF) {
if (ch!= '\n')
buffer[i++] = ch;
else {
buffer[i] = '\0';
d = atof(buffer);
if (d > dMax) dMax = d;
if (d < dMin) dMin = d;
i = 0;
}
}
Personally, I'll solve the problem in this way
int BestResult(FILE *infile) {
char buf[100];
int len=0;
int i=0;
while(fgets(buf,sizeof(buf),infile)!=NULL)
len++;
rewind(infile);
int *numbers;
numbers = (int *) malloc (len * sizeof(int));
while(fgets(buf,sizeof(buf),infile)!=NULL)
sscanf(buf,"%i",&numbers[i]);
int maxnum=numbers[0];
for(i=1;i<len;i++)
if(numbers[i]>maxnum)
maxnum=numbers[i];
return maxnum;
}
So first read the size of the file that defines the number of elements in your case, then allocate an array of integers to the number of items you find, fill the array by parsing line by line with the fgets and fill the array with sscanf, then research the maxnumber.

C programming - sscanf for tkens separated by space

I'm currently trying writing a program using C (very new to C - only been learning it for 2 weeks), and I wanted to get a string of input from the user by stdin, in which the string has a char, followed by 2 floats (each has space in between). Example would be: "y 2.1 1.1".
My question is how can I obtain and store the 3 inputs, while making sure the first is a char, and the following two inputs are floats?
Stick with sscanf(), but don't forget to check its return value (look here). What really happens for input "y 1u 1" is that sscanf will read and store the char, which is valid, then it will read and store the int 1, which is valid, and then stop, because "u" does not match the format string.
Below is example code (using scanf() rather then fgets() and sscanf()).
char in1;
int in2,in3;
int retval;
/*
char array[100] = {'\0'};
fgets(array, 100, stdin);
retval = sscanf(array, "%c %d %d", &in1, &in2, &in3);
*/
retval = scanf("%c %d %d", &in1, &in2, &in3);
printf("Scanned %d items\n", retval);
printf("Here they come: ");
if(retval > 0) {
printf("%c ", in1);
}
if(retval > 1) {
printf("%d ", in2);
}
if(retval > 2) {
printf("%d", in3);
}
putchar('\n');
How can I obtain and store the 3 inputs, while making sure the first is a char, and the following two inputs are ints?
problem with this code is that there are extra spaces at the very end, and I don't know how to get rid of it.
A simple way to use sscanf() and check if there is extra anything after the scanned variable is to use "%n" to record the location of the scan at that point.
char in1;
int in2, in3;
int n = 0;
sscanf(array,"%c %d %d%n", &in1, &in2, &in3, &n);
if (n > 0 && (array[n] == '\n' || array[n] == '\0')) {
Success(in1, in2, in3);
}
It is always important to check the results of sscanf(). One way is to check its return value which should be 3 here. Unfortunately that does not tell us if anything exist after in3. By setting n == 0 and then testing n > 0, code knows that scanning proceeded all the way successfully to "%n". Code can also test what character the scanning stopped at.

Get char at a defined location in a string in C

I'm looking to read in a number from the keyboard and then I have to manipulate each digit individually (it's an Octal to Decimal converter).
Is there something similar to the charAt() method from Java that can be used to work with s particular digit?
I currently have the below code (incomplete) but when compiling, it returns "error: subscripted value is neither array nor pointer"
#include <stdio.h>
#include <math.h>
#include <string.h>
int main()
{
printf("Please enter an octal number ending with #");
char nextNum = getchar();
char number;
int counterUp = 0; //Records how many digits are entered
int counterDown = 1; //Records progress during conversion
int decimalNumber = 0;
while(nextNum != '#') //reads in the whole number, putting the characters together to form one Octal number.
{
number = (number + nextNum);
counterUp++;
nextNum = getchar();
}
//Begin converson from Octal to Decimal
while(counterUp >= 0)
{
int added = (number[counterUp] * (pow(8, counterDown)));
decimalNumber = (decimalNumber + added);
counterDown++;
}
}
I'm not looking to be told how to go from octal to decimal, just how to work with one digit at a time.
Use fgets() instead of a single char:
char number[25]; // max of 25 characters in string
fgets(number, 24, stdin); // read a line from 'stdin', with a max of 24 characters
number[24] = '\0'; // append the NUL character, so that we don't run into problems if we decide to print the string
Now you can subscript number at will, e.g. number[10] = 'A'.
I think you're used to Java way where you can write something like:
String number = "";
number += "3";
number += "4";
Strings in C do not work like that. This code doesn't do what you think it does:
char number = 0; // 'number' is just a one-byte number
number += '3'; // number now equals 51 (ASCII 3)
number += '4'; // number now equals 103 (meaningless)
Maybe something like this will work for you:
char number[20];
int i = 0;
number[i++] = '3';
number[i++] = '4';
Or, you could simply use scanf to read a number in from the keyboard.
I recommend that you find a good book about C and read about strings first, then scanf second.
I think you need to step back and look at your algorithm more closely.
What does char number store? What do you expect this loop to do:
while(nextNum != '#') //reads in the whole number, putting the characters together to form one Octal number.
{
number = (number + nextNum);
counterUp++;
nextNum = getchar();
}
In particular, what does number = (number + nextNum); mean?
You need to define number as an array of chars.
e.g.
char number[16];
Then change your reading loop to append to the array.
while(nextNum != '#')
{
number[counterUp] = nextNum;
counterUp++;
nextNum = getchar();
}

Putting numbers separated by a space into an array

I want to have a user enter numbers separated by a space and then store each value as an element of an array. Currently I have:
while ((c = getchar()) != '\n')
{
if (c != ' ')
arr[i++] = c - '0';
}
but, of course, this stores one digit per element.
If the user was to type:
10 567 92 3
I was wanting the value 10 to be stored in arr[0], and then 567 in arr[1] etc.
Should I be using scanf instead somehow?
There are several approaches, depending on how robust you want the code to be.
The most straightforward is to use scanf with the %d conversion specifier:
while (scanf("%d", &a[i++]) == 1)
/* empty loop */ ;
The %d conversion specifier tells scanf to skip over any leading whitespace and read up to the next non-digit character. The return value is the number of successful conversions and assignments. Since we're reading a single integer value, the return value should be 1 on success.
As written, this has a number of pitfalls. First, suppose your user enters more numbers than your array is sized to hold; if you're lucky you'll get an access violation immediately. If you're not, you'll wind up clobbering something important that will cause problems later (buffer overflows are a common malware exploit).
So you at least want to add code to make sure you don't go past the end of your array:
while (i < ARRAY_SIZE && scanf("%d", &a[i++]) == 1)
/* empty loop */;
Good so far. But now suppose your user fatfingers a non-numeric character in their input, like 12 3r5 67. As written, the loop will assign 12 to a[0], 3 to a[1], then it will see the r in the input stream, return 0 and exit without saving anything to a[2]. Here's where a subtle bug creeps in -- even though nothing gets assigned to a[2], the expression i++ still gets evaluated, so you'll think you assigned something to a[2] even though it contains a garbage value. So you might want to hold off on incrementing i until you know you had a successful read:
while (i < ARRAY_SIZE && scanf("%d", &a[i]) == 1)
i++;
Ideally, you'd like to reject 3r5 altogether. We can read the character immediately following the number and make sure it's whitespace; if it's not, we reject the input:
#include <ctype.h>
...
int tmp;
char follow;
int count;
...
while (i < ARRAY_SIZE && (count = scanf("%d%c", &tmp, &follow)) > 0)
{
if (count == 2 && isspace(follow) || count == 1)
{
a[i++] = tmp;
}
else
{
printf ("Bad character detected: %c\n", follow);
break;
}
}
If we get two successful conversions, we make sure follow is a whitespace character - if it isn't, we print an error and exit the loop. If we get 1 successful conversion, that means there were no characters following the input number (meaning we hit EOF after the numeric input).
Alternately, we can read each input value as text and use strtol to do the conversion, which also allows you to catch the same kind of problem (my preferred method):
#include <ctype.h>
#include <stdlib.h>
...
char buf[INT_DIGITS + 3]; // account for sign character, newline, and 0 terminator
...
while(i < ARRAY_SIZE && fgets(buf, sizeof buf, stdin) != NULL)
{
char *follow; // note that follow is a pointer to char in this case
int val = (int) strtol(buf, &follow, 10);
if (isspace(*follow) || *follow == 0)
{
a[i++] = val;
}
else
{
printf("%s is not a valid integer string; exiting...\n", buf);
break;
}
}
BUT WAIT THERE'S MORE!
Suppose your user is one of those twisted QA types who likes to throw obnoxious input at your code "just to see what happens" and enters a number like 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 which is obviously too large to fit into any of the standard integer types. Believe it or not, scanf("%d", &val) will not yak on this, and will wind up storing something to val, but again it's an input you'd probably like to reject outright.
If you only allow one value per line, this becomes relatively easy to guard against; fgets will store a newline character in the target buffer if there's room, so if we don't see a newline character in the input buffer then the user typed something that's longer than we're prepared to handle:
#include <string.h>
...
while (i < ARRAY_SIZE && fgets(buf, sizeof buf, stdin) != NULL)
{
char *newline = strchr(buf, '\n');
if (!newline)
{
printf("Input value too long\n");
/**
* Read until we see a newline or EOF to clear out the input stream
*/
while (!newline && fgets(buf, sizeof buf, stdin) != NULL)
newline = strchr(buf, '\n');
break;
}
...
}
If you want to allow multiple values per line such as '10 20 30', then this gets a bit harder. We could go back to reading individual characters from the input, and doing a sanity check on each (warning, untested):
...
while (i < ARRAY_SIZE)
{
size_t j = 0;
int c;
while (j < sizeof buf - 1 && (c = getchar()) != EOF) && isdigit(c))
buf[j++] = c;
buf[j] = 0;
if (isdigit(c))
{
printf("Input too long to handle\n");
while ((c = getchar()) != EOF && c != '\n') // clear out input stream
/* empty loop */ ;
break;
}
else if (!isspace(c))
{
if (isgraph(c)
printf("Non-digit character %c seen in numeric input\n", c);
else
printf("Non-digit character %o seen in numeric input\n", c);
while ((c = getchar()) != EOF && c != '\n') // clear out input stream
/* empty loop */ ;
break;
}
else
a[i++] = (int) strtol(buffer, NULL, 10); // no need for follow pointer,
// since we've already checked
// for non-digit characters.
}
Welcome to the wonderfully whacked-up world of interactive input in C.
Small change to your code: only increment i when you read the space:
while ((c = getchar()) != '\n')
{
if (c != ' ')
arr[i] = arr[i] * 10 + c - '0';
else
i++;
}
Of course, it's better to use scanf:
while (scanf("%d", &a[i++]) == 1);
providing that you have enough space in the array. Also, be careful that the while above ends with ;, everything is done inside the loop condition.
As a matter of fact, every return value should be checked.
scanf returns the number of items successfully scanned.
Give this code a try:
#include <stdio.h>
int main()
{
int arr[500];
int i = 0;
int sc = 0; //scanned items
int n = 3; // no of integers to be scanned from the single line in stdin
while( sc<n )
{
sc += scanf("%d",&arr[i++]);
}
}

Resources