A
Albert
#include said:#include <stdlib.h>
int main()
I forgot the function prototype
int read_num(FILE *);
Oops.
#include said:#include <stdlib.h>
int main()
Albert said:Here's a really easy problem:
The input will consist of a single line containing a single integer.
If I may copy Ben's supplied number-input function
int read_num(FILE *fp)
{
static unsigned long line = 0;
int n;
line += 1;
char first[2];
if (fscanf(fp, "%1[0-9+-]", first) == 1 &&
ungetc(first[0], fp) != EOF &&
fscanf(fp, "%d\n", &n) == 1)
return n;
fprintf(stderr, "Input error on line %lu.\n", line);
exit(EXIT_FAILURE);
}
If there is not need to check the validity of the input (i.e. if there
are not invalid inputs in the test cases) then a simple scanf("%d",
&n) is all you need.
fscanf(fp, "%1[\n]", &n, first) == 1)
Albert said:Yes, but Richard Heathfield wants me (I presume) to program like a
real-world programmer.
fscanf(fp, "%1[\n]", &n, first) == 1)
Why are you allowed to pass two addresses when you only have one %?
Both spellings are accepted in different parts of the English speaking
world;
Albert said:Yes, but I like to point out that I like initialised with a 's'
instead of a 'z' when others use a 'z'.
Do you really pronounce it with a hard "s", that is, an unvoiced
consonant, like "initial iced"? To me (native speaker of American
English) that is hard to say and sounds unnatural, and I can't recall
hearing a native speaker of any other dialect pronounce it that way.
int read_num(FILE *fp)
{
static unsigned long line = 0;
int n;
line += 1;
char first[2];
if (fscanf(fp, "%1[0-9+-]", first) != 1 ||
ungetc(first[0], fp) == EOF ||
fscanf(fp, "%d\n", &n) != 1) {
fprintf(stderr, "Input error on line %lu.\n", line);
<snip rest>that value N might be on line N or line M !!= N. ("very unequal said:exit(EXIT_FAILURE);
}
return n;
}
David Thompson said:On Sat, 10 Jan 2009 22:58:16 -0800 (PST), Albert
int read_num(FILE *fp)
{
static unsigned long line = 0;
int n;
line += 1;
char first[2];
if (fscanf(fp, "%1[0-9+-]", first) != 1 ||
ungetc(first[0], fp) == EOF ||
fscanf(fp, "%d\n", &n) != 1) {
fprintf(stderr, "Input error on line %lu.\n", line);
Note that the \n in *scanf does not look for specifically a newline,
but for any whitespace, which could be multiple newlines or none.
Only if the input is always one integer per line does this correctly
count lines -- and if you get the error, that's a pretty strong clue
that the input format wasn't right. At a minimum I would make the
message "error on >value< #" and leave it for the person to understand
that value N might be on line N or line M !!= N. ("very unequal"
<G>)
Albert said:Yes, but Richard Heathfield wants me (I presume) to program like a
real-world programmer.
Richard said:Then I suggest that, in this simple case, you use fgets() and
strtol() instead. That combination is more easily bullet-proofed
than any *scanf() call, and it more than suffices in this case.
Nate said:Do you really pronounce it with a hard "s", that is, an unvoiced
consonant, like "initial iced"? To me (native speaker of American
English) that is hard to say and sounds unnatural, and I can't
recall hearing a native speaker of any other dialect pronounce it
that way.
CBFalconer said:I disagree. getc() is an even better interface, and allows very
detailed error checking. For example, the following code can do
the input, will signal all overflows,
Richard said:.... snip ...
You're insane. That following code was _two hundred_ fecking
lines! And all that to read one single line containing one single
integer which must be between 1 and 10000? When any line differing
from that can be flagged as an error, without needing further
processing? Using even two dozen lines of code for that job is
reckless overkill; using two hundred is Microsoft-level bloatware.
CBFalconer said:No, it was source code with an adequate set of comments, meant to
provide a reliable method of extracting numbers from input
streams. If you count lines in the fundamental input routine, I
think you will find 24. And note that the interface uses getc,
ungetc, and ctype. It detects and signals overflow, thus making it
useful in other areas, such as readxint.
I think you will find it much shorter than the mess loaded from a
library to implement scanf.
Then I suggest that, in this simple case, you use fgets() and strtol()
instead. That combination is more easily bullet-proofed than any
*scanf() call, and it more than suffices in this case.
Albert said:On Jan 21, 7:27Â am, (e-mail address removed) (Richard Bos) wrote:
Then is the following better than the function after it?
A reminder:
knowing roughly how far the program gets to before an error occurs is
usually enough to debug clever algorithms for the purpose of gaining
marks in a programming contest.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define MAXLINELEN 10
#define BASE 10
int read_num(FILE* in)
{
long num;
static unsigned long ncalls = 0;
ncalls += 1;
char str[MAXLINELEN];
if (fgets(str, MAXLINELEN, in) == NULL) {
fprintf(stderr, "Reading input error on call no. %d to
read_num.\n", ncalls);
exit(EXIT_FAILURE);
}
num = strtol(str, NULL, BASE);
if (num == 0 && str[0] != 0) { // If input was zero, then strtol
returning zero is OK
fprintf(stderr, "No conversion performed on call no. %d to
read_num.\n", ncalls);
exit(EXIT_FAILURE);
} else if ((num == LONG_MAX || num == LONG_MIN) && errno ==
ERANGE) {
fprintf(stderr, "Input would overflow a long on call no. %d to
read_num.\n", ncalls);
exit(EXIT_FAILURE);
}
return (int) x;
}
#include <stdio.h>
#include <stdlib.h>
int read_num(FILE *fp)
{
static unsigned long ncalls = 0;
int n;
ncalls += 1;
char first[2];
if (fscanf(fp, "%1[0-9+-]", first) == 1 &&
ungetc(first[0], fp) != EOF &&
fscanf(fp, "%d\n", &n) == 1) {
return n;
}
fprintf(stderr, "Input error on call no. to read_num\n", ncalls);
exit(EXIT_FAILURE);
}
And BTW, has Richard Heathfield been posting regularly in
comp.lang.c?
There is only an answer to such questions if enough can be said about
the criteria for evaluating "better". I will offer a few comments
anyway. Only you can decide which one is better.
exit(EXIT_FAILURE);
}num = strtol(str, NULL, BASE);
if (num == 0 && str[0] != 0) { // If input was zero, then strtol
returning zero is OK
I don't see how it can be an error when num == 0 and str[0] != 0. Did
you mean != '0'? even so, what about "+0" and "-0"? strtol will also
accept " 0" and return zero. If you want to stop this, your need some
other sort of test.
Albert said:I'll modify the question to provide a slightly more detailed question:
If you HAD to choose between the two functions above to utilise in a
programming contest, which one would you choose and why?
    exit(EXIT_FAILURE);
  }  num = strtol(str, NULL, BASE);
  if (num == 0 && str[0] != 0) { // If input was zero, then strtol
returning zero is OK
I don't see how it can be an error when num == 0 and str[0] != 0. Â Did
you mean != '0'? Â even so, what about "+0" and "-0"? Â strtol will also
accept " 0" and return zero. Â If you want to stop this, your need some
other sort of test.
Would the pseudocode be then
IF NUM = 0 AND STR[0] != '0' AND STR[0] != '+0' AND STR[0] != '-0'
...HANDLE ERRROR
to stop an error message appearing because 0, +0 or -0 was inputted?
Ben Bacarisse said:Albert said:int read_num(FILE* in)
{
long num;
static unsigned long ncalls = 0;
ncalls += 1;
char str[MAXLINELEN];
if (fgets(str, MAXLINELEN, in) == NULL) {
fprintf(stderr, "Reading input error on call no. %d to
read_num.\n", ncalls);
You need %lu when printing unsigned long here (and in two places
below).
exit(EXIT_FAILURE);
}
num = strtol(str, NULL, BASE);
if (num == 0 && str[0] != 0) { // If input was zero, then strtol
returning zero is OK
I don't see how it can be an error when num == 0 and str[0] != 0.
Did you mean != '0'? even so, what about "+0" and "-0"? strtol will
also accept " 0" and return zero. If you want to stop this, your need
some other sort of test.
You risk all sorts of trouble if long can hold larger values than
int. It is probably better to test against INT_MAX and INT_MIN so you
can report a result that overflows rather than just returning it.
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.