Best way to get numbers from input?
Asked Answered
M

2

0

I would like to loop character by character from an input file that has text and numbers.

I thought I could just loop

char count;

while( c != ' ' && c != '\n' && c != '\t' ) {
  count += c;
  c = fgetc(fp);
}

To say get "11" from the text file and then use atoi() to convert to an int but I then realized I'm just adding ASCII numbers.

I'm fairly new to C and from what I understand strings are just char arrays - does this mean I have to put "1" and "1" into a char array?

But then I would have to worry about array size and converting that to a number?

Mayne answered 3/2, 2016 at 8:20 Comment(3)
The C specification says that numerical digits always have to be consecutive, no matter the encoding. That means you can do e.g. '1' - '0' to get the integer value 1. From there it's easy to create integer values no matter the base used for the input. No need to store in a string for later use with atoi. If you don't want to construct the number on the fly, and use atoi, this comment gives you a hint about that too.Cynicism
Quick fix is to do count += c - '0';Right
Lundin, when I made that change, it converted "11" to 2Mayne
U
4

While it looks like the natural solution, I usually advice against using fscanf() on potentially malformed input.

There are several issues with the function, among them:

  • The inability to recover gracefully from an error (short of ftell() / fseek()), because you don't know where exactly it stopped parsing input.
  • The ease with which it leads to using uninitialized values (if the format failed to match the input, and you didn't check the return code properly).
  • Some corner cases where fscanf() chokes on input where atoi() / strtol() don't ("0xz"...).

All this relegates fscanf() to read well-formatted input only, i.e. things your own program had written before in a known format.

For any kind of input that might not be in the expected format, I recommend reading a line at a time (fgets()), and parsing it in-memory, for example with strtol():

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define LINE_BUFFER_SIZE 256;

// ...
char line[ LINE_BUFFER_SIZE ];
fgets( line, LINE_BUFFER_SIZE, fp );
char * scanptr;
errno = 0;
long number = strtol( line, &scanptr, 0 );
if ( scanptr == line )
{
    // not a number...
}
  • scanptr now points to where number parsing ended.
  • number contains the number parsed, 0 if line did not start with a number, and LONG_MAX resp. LONG_MIN if the number found is out of long range (in which case errno == ERANGE as well).

I don't know your exact requirements, so I cannot give a good example for what to do if you don't find a number, or what to do with the text. But since you got the line (or, the first LINE_BUFFER_SIZE characters of a longer line...) in memory, you have the whole range of the string functions at your disposal. strpbrk( line, "0123456789" ) could be used to scan for the next digit, or strpbrk( line, "+-0123456789" ) if you might have +- in your input...

Upchurch answered 3/2, 2016 at 8:59 Comment(7)
Even though variations of this question come up from time to time and similar answers discussing *scanf() and strto*(), this is one of the best ones yet for a learner.Pebbly
@chux: Thanks. Together with "what is Unicode, and how do I use it in C/C++", it's one of those I've been thinking about writing a FAQ entry for and never got around to doing it (yet). Seems I answer one of them every other week. ;-)Upchurch
Contact me should you embark on that and want a (pre-)review.Pebbly
Thanks DevSolar. Im trying to grasp what you wrote simply because of unfamiliarity with C. I need to iterate through a file and find numbers and words that I will later use. Fgetc seemed to be the most natural since I can continue from where I last found something. I should be able to use scanptr for that? Part of me is thinking of switching to C++ so I can just work with strings but feels like I'm quitting if I do haMayne
@cpd1: Going for C++ is not "quitting". It's a much more powerful language that makes many things much easier -- but comes with its own pitfalls. Generally speaking, while it's a good thing to work along some "toy" application while learning a language, it's not a good idea to try both learning a languagne and solving a "real" problem at the same time. You want to first get a "feel" for a language, and the tools it provides, so you can select the right tools for any given problem. Otherwise, you'll always play blind man's bluff, and become frustrated.Upchurch
I hear you and I wish I had more time to learn before I try coding up something but I really don't. This is just a small piece of something larger I have to put together and I've spent the last few days trying to get my head around the parsing part. Likely because I'm thinking of it as how I would handle it in Java with strings etcMayne
I hoped the scope of that Q&A would have been much smaller along the lines of "How to read / parse an integer in C?" - it is fairly broad.Pebbly
L
1

Most people would use fscanf:

int number, items_scanned;
items_scanned = fscanf(fp, "%d", &number);
if (items_scanned == 0) {
     /* Scanning for a number failed */
     printf("Scan failed!\n");
}
printf("Found number: %d\n", number);

fscanf skips whitespace. It will succeed if it finds a number and fail if it finds something else.

Laspisa answered 3/2, 2016 at 8:29 Comment(2)
Thanks Klas. I have to actually iterate through the file to find other text and numbers while keeping track of them so I would need some sort of line position with the above?Mayne
You can use ftell to find the current position in the file. You should also consider the answer by @Upchurch which may be a better fit for your problem.Asiatic

© 2022 - 2024 — McMap. All rights reserved.