How to check for errors when inputting a number

A

Albert

http://groups.google.com/group/comp...ca39c45ce7b/124648c23bd40ace#124648c23bd40ace
was a thread I started, back when I didn't know what a priority queue
was and how to implement a binary heap. After putting in some work, I
eventually figured out how create one and my personal best score for
AFL, the informatics problem requiring this data structure, is now 99%
(for 3 of the test cases it outputs on average only 70% correct
answers, I think); my solution is now very fast compared to the O(N^2)
solution I kept trying to debug for the duration of the above thread.

I admit, informatics temporarily changed my view of programming: the
idea was to come up with clever algorithms that scored 100% on the
test data given a limited input range. I've since been more interested
in the actual programming language itself, so I've come back to
checking for errors. Ben posted early on in the thread that a
convenient (is that a good adjective, Ben?) function for reading in a
number would be:

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);
exit(EXIT_FAILURE);
}
return n;

}

I've got a few questions about this:
1. Why does one variable have the static keyword in front of it and
not others?
2. If variable long's value is going to be incremented 1 two lines
later, why is it initialised (with an s) to 0?
3. I simply don't understand %1[0-9+-] [colon] could someone explain
this to me please?
4. Is stderr to the console?
5. What value does one usually set the constant EXIT_FAILURE to?

Thanks In Advance (TIA)
Albert
 
J

James Kuyper

Albert said:
http://groups.google.com/group/comp...ca39c45ce7b/124648c23bd40ace#124648c23bd40ace ....
checking for errors. Ben posted early on in the thread that a
convenient (is that a good adjective, Ben?) function for reading in a
number would be:

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);
exit(EXIT_FAILURE);
}
return n;

}

I've got a few questions about this:
1. Why does one variable have the static keyword in front of it and
not others?

From your comments below, it appears that what you really means is
"what does static mean?"

Unfortunately, the keyword 'static' has several different completely
unrelated meanings, depending upon where it appears. When it appears at
block scope (inside a function body) as it does above, what it means is
that the variable 'line' has 'static storage duration'. As a result,
'line' is initialized at the start of the program to the specified value
of 0 (which is redundant, because objects with static storage duration
are always initialized with 0 unless a different initializer is
explicitly specified). The variable 'line' remains in existence for the
entire time the program is running. Whatever value it had at the end of
one call to read_num() is the same value it will have at the start of
the next call to read_num().
2. If variable long's value is going to be incremented 1 two lines
later, why is it initialised (with an s) to 0?

There's no variable named 'long'. that's the type of the variable. The
variable's name is 'line'. I assume 's' is an abbreviation for 'static'?
Don't use such abbreviations, they're too obscure, and people might
not be able to figure out what it is your abbreviating (particularly if
you're NOT using it as an abbreviation for 'static').

The initialization to 0 occurs only once, at the beginning of the
program, before the first call to read_num(). That's one of the
consequences of declaring the variable 'static'. The increment, on the
other hand, occurs every time read_num() is called.
3. I simply don't understand %1[0-9+-] [colon] could someone explain
this to me please?

That's a format specifier for fscanf(). the '1' indicates that a maximum
of 1 character should be read. The '[' indicates that only characters in
the specified scan set should be read. The scan set is all of the
characters between the '[' and ']'. There's a special notation used in
scan sets: "0-9" is an abbreviation for "0123456789".

Scan sets are a pretty complicated feature of the language; if you're
still confused about 'static', I'd recommend putting off any study of
scan sets until later.
4. Is stderr to the console?

It might be, that depends upon how the program is used. I use the tcsh
shell on Irix and Linux machines, which makes it easy to send stderr to
the same file as stdout. In other common shells for Unix-like systems,
it's easy to send stdout and stderr to different files, or to send one
to the console and the other to a file. I no longer remember what
options you have with operating systems that aren't similar to Unix; its
been a dozen years since I programmed for a DOS machine, and longer than
that for VMS; I remember only that VMS had a VERY different idea about
command line interfaces than Unix does.
5. What value does one usually set the constant EXIT_FAILURE to?

You don't set it to anything. The implementation sets the value in
<stdlib.h>. There is no "usual value", the value can be different on
different operating systems. If you need to know what particular value
it has on your operating system, look for stdlib.h, if it's present.
Some implementations don't provide stdlib.h as an actual file that you
can read - in that case, just write a little program to print out the
value. However, you can't make any portable use of that information.

In portable code, the only thing you should think about when you see
EXIT_FAILURE is that it expands to an expression which has a value of
type 'int' which, if passed to exit() (or returned from main(), which
has the same effect), indicates to the calling environment that the
program failed.
 
C

CBFalconer

Albert said:
.... snip ...

I admit, informatics temporarily changed my view of programming:
the idea was to come up with clever algorithms that scored 100%
on the test data given a limited input range. I've since been
more interested in the actual programming language itself, so
I've come back to checking for errors. Ben posted early on in
the thread that a convenient (is that a good adjective, Ben?)
function for reading in a number would be:

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);
exit(EXIT_FAILURE);
}
return n;
}

I suggest you avoid using the scanf functions. They are especially
awkward for interactive use.

The following is a module I created some time ago. I am not happy
with readxint. It (and readxwd) should also be altered to use at
least longs, after which all the others (apart from long long) can
be easily generated.

/* ------------------------------------------------- *
* File txtinput.c *
* ------------------------------------------------- */

#include <limits.h> /* xxxx_MAX, xxxx_MIN */
#include <ctype.h> /* isdigit, isblank, isspace */
#include <stdio.h> /* FILE, getc, ungetc */
#include "txtinput.h"

/* For licensing restrictions (GPL) see readme.txt in:
* <http://cbfalconer.home.att.net/download/txtio.zip>
*
* These stream input routines are written so that simple
* conditionals can be used:
*
* if (readxint(&myint, stdin)) {
* do_error_recovery; normally_abort_to_somewhere;
* }
* else {
* do_normal_things; usually_much_longer_than_bad_case;
* }
*
* They allow overflow detection, and permit other routines to
* detect the character that terminated a numerical field. No
* string storage is required, thus there is no limitation on
* the length of input fields. For example, a number entered
* with a string of 1000 leading zeroes will not annoy these.
*
* The numerical input routines *NEVER* absorb a terminating
* char (including '\n'). Thus a sequence such as:
*
* err = readxint(&myint, stdin);
* flushln(stdin);
*
* will always consume complete lines, and after execution of
* readxint a further getc (or fgetc) will return the character
* that terminated the numeric field.
*
* They are also re-entrant, subject to the limitations of file
* systems. e.g interrupting readxint(v, stdin) operation with
* a call to readxwd(wd, stdin) would not be well defined, if
* the same stdin is being used for both calls. If ungetc is
* interruptible the run-time system is broken.
*
* Originally issued 2002-10-07
*
* Revised 2006-01-15 so that unsigned entry overflow (readxwd)
uses the normal C modulo (UINT_MAX + 1) operation. readxwd
still rejects an initial sign as an error.
*
* Modified to allow free use - C.B. Falconer 2008-01-16
*/

/* --------------------------------------------------------------
* Skip to non-blank on f, and return that char. or EOF. The next
* char that getc(f) will return is unknown. Local use only.
*/
static int ignoreblks(FILE *f)
{
int ch;

do {
ch = getc(f);
} while ((' ' == ch) || ('\t' == ch));
/* while (isblank(ch)); */ /* for C99 */
return ch;
} /* ignoreblks */

/*--------------------------------------------------------------
* Skip all blanks on f. At completion getc(f) will return
* a non-blank character, which may be \n or EOF
*
* Skipblks returns the char that getc will next return, or EOF.
*/
int skipblks(FILE *f)
{
return ungetc(ignoreblks(f), f);
} /* skipblks */

/*--------------------------------------------------------------
* Skip all whitespace on f, including \n, \f, \v, \r. At
* completion getc(f) will return a non-blank character, which
* may be EOF
*
* Skipwhite returns the char that getc will next return, or EOF.
*/
int skipwhite(FILE *f)
{
int ch;

do {
ch = getc(f);
} while (isspace(ch));
return ungetc(ch, f);
} /* skipwhite */

/*--------------------------------------------------------------
* Read an unsigned value. Signal error for overflow or no
* valid number found. Returns 1 for error, 0 for noerror, EOF
* for EOF encountered before parsing a value.
*
* Skip all leading blanks on f. At completion getc(f) will
* return the character terminating the number, which may be \n
* or EOF among others. Barring EOF it will NOT be a digit. The
* combination of error, 0 result, and the next getc returning
* \n indicates that no numerical value was found on the line.
*
* If the user wants to skip all leading white space including
* \n, \f, \v, \r, he should first call "skipwhite(f);"
*
* Peculiarity: This specifically forbids a leading '+' or '-'.
*/
int readxwd(unsigned int *wd, FILE *f)
{
unsigned int value, digit;
int status;
int ch;

#define UWARNLVL (UINT_MAX / 10U)
#define UWARNDIG (UINT_MAX - UWARNLVL * 10U)

value = 0; /* default */
status = 1; /* default error */

ch = ignoreblks(f);

if (EOF == ch) status = EOF;
else if (isdigit(ch)) status = 0; /* digit, no error */

while (isdigit(ch)) {
digit = ch - '0';
if ((value > UWARNLVL) ||
((UWARNLVL == value) && (digit > UWARNDIG))) {
status = 1; /* overflow */
value -= UWARNLVL;
}
value = 10 * value + digit;
ch = getc(f);
} /* while (ch is a digit) */

*wd = value;
ungetc(ch, f);
return status;
} /* readxwd */

/*--------------------------------------------------------------
* Read a signed value. Signal error for overflow or no valid
* number found. Returns true for error, false for noerror. On
* overflow either INT_MAX or INT_MIN is returned in *val.
*
* Skip all leading blanks on f. At completion getc(f) will
* return the character terminating the number, which may be \n
* or EOF among others. Barring EOF it will NOT be a digit. The
* combination of error, 0 result, and the next getc returning
* \n indicates that no numerical value was found on the line.
*
* If the user wants to skip all leading white space including
* \n, \f, \v, \r, he should first call "skipwhite(f);"
*
* Peculiarity: an isolated leading '+' or '-' NOT immediately
* followed by a digit will return error and a value of 0, when
* the next getc will return that following non-digit. This is
* caused by the single level ungetc available.
*
* This module needs further work. CBF 2008-01-16
*/
int readxint(int *val, FILE *f)
{
unsigned int value;
int status, negative;
int ch;

*val = value = 0; /* default */
status = 1; /* default error */
negative = 0;

ch = ignoreblks(f);

if (EOF != ch) {
if (('+' == ch) || ('-' == ch)) {
negative = ('-' == ch);
ch = ignoreblks(f); /* absorb any sign */
}

if (isdigit(ch)) { /* digit, no error */
ungetc(ch, f);
status = readxwd(&value, f);
ch = getc(f); /* This terminated readxwd */
}

if (0 == status) {
/* got initial digit and no readxwd overflow */
if (!negative && (value <= INT_MAX))
*val = value;
else if (negative && (value < UINT_MAX) &&
((value - 1) <= -(1 + INT_MIN)))
*val = -value;
else { /* overflow */
status = 1; /* do whatever the native system does */
if (negative) *val = -value;
else *val = value;
}
}
else if (negative) *val = -value;
else *val = value;
}
ungetc(ch, f);
return status;
} /* readxint */

/*-----------------------------------------------------
* Flush input through an end-of-line marker inclusive.
*/
void flushln(FILE *f)
{
int ch;

do {
ch = getc(f);
} while (('\n' != ch) && (EOF != ch));
} /* flushln */

/* End of txtinput.c */
 
B

Ben Bacarisse

Sounds fine by me. BTW, I marked you post in order to repl, but my
feed went down and when it came back James are answered better that I
would have done!
function for reading in a number would be:

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);
exit(EXIT_FAILURE);
}
return n;
}

I suggest you avoid using the scanf functions. They are especially
awkward for interactive use.

I like them because they are useful. The above code was not intended
for interactive use (hence the line error report and the call to exit).

Are you suggesting replacing the dozen lines above with the, what?,
the 70 or so lines below that you are not yet happy with? (Also, it
does not keep track of lines, so it can not be a drop-in replacement.)

You are close to making the case for how much simpler it is not use
fscanf! BTW, I know the line counting in my function is defective in
general but it works in this case where only the firs error get reported.

<snip 227 lines of code -- mostly comment which is a good thing, or course>
 
C

CBFalconer

Ben said:
Sounds fine by me. BTW, I marked you post in order to repl, but
my feed went down and when it came back James are answered better
that I would have done!

I see no post from James later than my post. !!
.... snip ...

I like them because they are useful. The above code was not
intended for interactive use (hence the line error report and the
call to exit).

Are you suggesting replacing the dozen lines above with the, what?,
the 70 or so lines below that you are not yet happy with? (Also, it
does not keep track of lines, so it can not be a drop-in replacement.)

My post was intended to give Albert ideas. The code is intended to
be part of a flexible numerical input system, not using scanf. My
dissatisfaction with readintx is the provisions for reading MAX_INT
and MIN_INT. I dislike loading a buffer with a line and parsing
that.
 
K

Kaz Kylheku

That's incorrect. According to 6.2.4{3}, "its stored value is
initialized only once, *prior* to program startup."

Asshole, can you write a strictly conforming C program that detects
whether a static variable was initialized "at" program startup or "prior to"?

You really must be from China, because in the English language, "at the
start" can be understood to mean just before the commencement.

"At the start of the project, we had twenty developers". What does that
sentence mean? Does it mean: "Prior to start, there was one developer, and in
the very nanosecond he wrote the first character of code, nineteen others
materialized?"

Prior to program startup could mean that the storage is initialized by a hook
that is inserted at the beginning of main:

// as if
int main(void)
{
__static_initialize(); hidden call

}

This could be described as "at the start of the program".

A strictly conforming C program can't have anything happening before main,
so there is no way to tell that initializations have been deferred.
A common misunderstanding. Lifetimes of objects with automatic
storage duration declared in main() persist through the call
to exit().

James' complete text:

J> In portable code, the only thing you should think about when you see
J> EXIT_FAILURE is that it expands to an expression which has a value of
J> type 'int' which, if passed to exit() (or returned from main(), which
J> has the same effect), indicates to the calling environment that the
J> program failed.

He didn't claim that exit and returning from main are exactly the same thing.
They ultimately have the same effect, which is true.

The storage duration of objects in main isn't an ``effect'', and isn't
relevant to the behavior of EXIT_FAILURE.

You really are lame.
 
B

Barry Schwarz

That's incorrect. According to 6.2.4{3}, "its stored value is
initialized only once, *prior* to program startup."


Incorrect. See above. There's a difference between "at the beginning
of the program" and "prior to program startup".


A common misunderstanding. Lifetimes of objects with automatic
storage duration declared in main() persist through the call
to exit().

No, automatic objects defined in main persist only for the life of the
current instance of main. Since main can be called recursively, some
returns from main need not invoke exit but the automatic objects still
cease to exist. Only the return from the "initial" main is equivalent
to calling exit.
 
K

Kaz Kylheku

[Reposted in-thread]

Kaz said:
He didn't claim that exit and returning from main are exactly the same thing.
They ultimately have the same effect, which is true.

Incorrect. The automatic storage duration of objects declared in
main() is very relevant, since in the case of setbuf() and such, the
effect of exit() can be fine while the effect of returning from
main() invokes undefined behavior.

This has to be contrived.

In programs that don't access main's automatic variables from atexit handlers,
and don't register such buffers, returning from main works.

When both methods of termination are well-defined, they have an equivalent
effect.

Sounds good to me.

Just like y = 1 and y = x/x are the same function over the domain that
excludes the singularity zero, which is not a complete equivalence, but
a functionally useful one.

Jack was explaining EXIT_FAILURE and how it can be either returned from main or
passed to exit.

There is absolutely no need to ramble about the irrelevant possibility that,
oh, returning from main is not like exit because it might break in some
degenerate programs which dole out main's automatic storage to the stdio
library or to atexit handlers.

There is no need to drag that out every time main and exit come up.

On the other hand, when main is being discussed, there is nothing more
important other than that it must return int, and be defined as taking either
no parameters, or exactly two of type int and char **.

Got that?
Other sophistry ignored. Very weak.

Right, so you're saying you picked the high-hanging fruit here? Sure!

I thoroughly destroyed your idiotic bullshit about "prior to start" versus "at
start".

If I had not done so, you'd be tripping over your sneakers to follow up
with another round.

All you could come up with is a continuation of this main versus exit
buffoonery.
 
J

James Kuyper

Kaz said:
Asshole, can you write a strictly conforming C program that detects
whether a static variable was initialized "at" program startup or "prior to"?

You really must be from China, because in the English language, "at the
start" can be understood to mean just before the commencement.

I've a Taiwanese wife, and work at a company where 40% of the employees
are foreign-born, mostly Chinese. I'm quite used to such mis-communications.

....
He didn't claim that exit and returning from main are exactly the same thing.
They ultimately have the same effect, which is true.

The storage duration of objects in main isn't an ``effect'', and isn't
relevant to the behavior of EXIT_FAILURE.

I actually considered both of these issues, and decided that if Albert
was still unclear about the meaning of 'static', it was probably not a
good idea to get into such esoteric details.

I also thought of giving Albert a complete survey of the other meanings
of 'static', but ended up deciding that this would be too big of a
lesson to digest at one time. In particular, the new use of 'static' for
function parameters that was introduced in C99 is something that he can
put off worrying about for quite some time.
 
B

Ben Bacarisse

CBFalconer said:
My post was intended to give Albert ideas.

Rewriting C's numerical input system is not an idea I'd like to give
someone starting out with C but, as they say, YMMV.
The code is intended to
be part of a flexible numerical input system, not using scanf. My
dissatisfaction with readintx is the provisions for reading MAX_INT
and MIN_INT. I dislike loading a buffer with a line and parsing
that.

So do I. That is why my code did not use sscanf.
 
K

Kaz Kylheku

Looks as if you took the wrong side in that argument. There's no
miscommunication. The standard differentiates between "at the start"
(your misuse) and "prior to the start" (correct use). This is perfectly
clear from clause 5, which I have quoted.

James originally wrote:

`` As a result, 'line' is initialized at the start of the program to the
specified value of 0 ...''

Note that in the English language, with which you are clearly not that
familiar, the past participle "initialized" serves as both verb and adjective.

The following sentence can be interpreted such that there is no logical
conflict:

``Prior to program start, x is initialized to zero, so that
at program start, x is initialized to zero.''

The first occurence of initialized may be interpreted as either a verb (action
of being initialized) or adjective (state of being initialized). The second
interpretation is strictly that of ajective. We can add the term ``already''
and it's still grammatically valid: ``at program startup, x is already
initialized to zero.'' See?

Anyway, we have the as-if rule in language semantics: if you can't write a
program which depends on some distinction in the spec, it's not a true
distinction. The distinction only exists in that area of semantics which has no
bearing on anything real.

There is no rational way that James' statement can be interpreted to mean that
``a C program can be written which can observe static storage in its
uninitialized state''.
 
C

CBFalconer

Kaz said:
.... snip ...

A strictly conforming C program can't have anything happening
before main, so there is no way to tell that initializations
have been deferred.

They can and do. That bit allows for 'reworking' the operating
system to suit the C system.
 
B

Barry Schwarz

I don't disagree with any of the above. If we're talking about a
recursive instance of main() -- bizarre, given the usual implications
of main() on this newsgroup -- then it would seem you have provided
a counterargument to James's point, since a recursive instance of
main() could call exit(EXIT_FAILURE), whereas a return of
EXIT_FAILURE from a recursive instance of main() could lead to
an outer main() calling exit(EXIT_SUCCESS). My point still stands.

No it falls on its face. It is entirely possible for automatic
objects defined in main to go out of existence without exit or the
equivalent return ever being invoked
That isn't true and requires only one counterexample, which is

Since it's what the standard says in 5.1.2.2.3, its validity is not
subject to your opinion.
as follows: Misuse of setbuf() can result in exit(EXIT_FAILURE)
being fine, whereas a return of EXIT_FAILURE invokes undefined
behavior. This is why the standard points out that the lifetimes

Since setbuf returns no value (7.19.5.5), how do you propose that it
return EXIT_FAILURE?
of objects with automatic storage duration declared in main()
persist through the call to exit(), a subtle but nevertheless
real difference. They're not equivalent.

Footnote 10 that makes this statement deals only with the initial main
and not with any recursively called main. Since the recursive calls
to main are not addressed separately, the provision of 6.2.4-5 apply.
See what happens when you're civil? You get a civil reply.

Civility is nice, accuracy would be better.
 
K

Kaz Kylheku

I can accept that, provided the pedants I've taken after are willing to
extend the same level of charitableness to others when it comes to
possible double meanings. You know damn well that people pick on
ambiguities on this newsgroup and choose the less charitable interpretation
in readers' posts when it comes to double meanings.

However, in this case, you have acted disingenuously. We have two statements
from James, and you stripped out one.

"As a result, 'line' is initialized at the start of the program to the
specified value of 0"

In this case, you can weasel your way into the adjectival form of the
past participle "initialized". I can accept that.

But the second statement

"The initialization to 0 occurs only once, at the beginning
of the program"

Now try to shoehorn "initialization TO 0" into the adjectival form of
the past participle "initialized". I dare you. "initialization" is
NOT a past participle; it's a nominalization that carries verbal
form.

Initialization is noun which names the action. It carries verbal
semantics not form, but never mind.

Thus James's posting cannot be rescued by appealing to ambiguity in the
participle form.

In the English language, there is a distinction between ``prior to''
and ``at''. In some domains of discourse, the difference may be critically
important. In other domains, ``prior to'' and ``at'' may in fact refer to the
same place.

So if you argue about the distinction, you are not really arguing about
anything in that domain, but about the English language.

What does it mean if we say ``Let us place a new statement at the
beginning of execution?''. Where is the new statement inserted? It's added
before the current first statement, so that in fact it executes prior to.

When we say that some event happens ``at'' the beginning of execution, and
execution is sequential, it's understood that it's the first thing that
happens. If we say that the event happens ``prior to'' the beginning of
execution, it's still the first thing that happens. Only, we are excluding it
from being /called/ ``execution''.

What we call it doesn't change what it does.

Remember that a dog's tail is still a tail even if we call ``leg''.

It doesn't matter one bit whether or not static variable initialization is
considered part of the program (at its start) or not part of the program
(prior to its start).

If your point is that technical nitpicking is wrong, you have chosen a bad
example, because there is no technical distinction here that can be picked
apart.

Real language pedants pick real distinctions to nitpick. A real distinction is
something that can be tested on some machine, where there is a different
outcome based on the disputed proposition being true or false. When the
required machine si only imaginary, then you have technical pedantry.
Arguing about whether tails should be called "leg" isn't the real thing.
 
K

Kenny McCormack

....
If your point is that technical nitpicking is wrong, you have chosen a
bad example, because there is no technical distinction here that can
be picked apart.

Real language pedants pick real distinctions to nitpick. A real
distinction is something that can be tested on some machine, where
there is a different outcome based on the disputed proposition being
true or false. When the required machine si only imaginary, then you
have technical pedantry. Arguing about whether tails should be called
"leg" isn't the real thing.

Wrong on all counts. The whole gist of CLC is to complain about totally
theoretical and completely untestable points. Think of how many times
they complain about something not working on the DS9000, a machine which
doesn't exist.

I repeat: that's the whole point of CLC. And, to bring up my main point
of consistency about it all: It is just like faith. If faith were
disprovable, it would disappear in a puff of logic. The goal of the
maintainers of faith is to keep it unprovable (one way or the other), so
that it can be maintained.

As usual, Han has you pinned to the table here, with no escape in sight.
 
A

Albert

That's a format specifier for fscanf(). the '1' indicates that a maximum
of 1 character should be read.

And that would obviously imply that I can read only positive integers
from 1-9 and 0 using this function. When I read the code back in the
AFL thread I thought it was an alternative to writing fscanf in main
()!

N.B. When I write 'with an s' it's opposed to the 'with a z'; a harder
to pronounce initialiZed as opposed to a much more fluent initialiSed.
So pay attention to a massive ego when it comes to various
'accepted' (??? accepted?) spellings of words.
 
J

jameskuyper

Albert said:
And that would obviously imply that I can read only positive integers
from 1-9 and 0 using this function.

Not quite. First of all, it's the format specifier "%1[0-9+-]" that
imposes such a restriction. The fscanf() function itself has no such
restriction. Secondly, that format specifier also allows you the read
the '+' and '-' characters, not just '0' through '9'. Finally, realize
that you're reading in characters. The character '7' counts as an
integer in C, but it has a value which, in the current character
encoding, represents the '7' character; this is generally not 7. To
read in integers, rather than characters, you need to use one of the
integer format specifications, like "%d" or "%u".
N.B. When I write 'with an s' it's opposed to the 'with a z'; a harder
to pronounce initialiZed as opposed to a much more fluent initialiSed.
So pay attention to a massive ego when it comes to various
'accepted' (??? accepted?) spellings of words.

Ah! so that's what you were talking about! It still doesn't quite make
sense, but it makes more sense.

Both spellings are accepted in different parts of the English speaking
world; I've spent enough time in Britain and enough time reading
British works that I have to rely upon my spell-checker to make sure I
produce the spelling that is appropriate on this side of the pond.
 
B

Ben Bacarisse

Albert said:
And that would obviously imply that I can read only positive integers
from 1-9 and 0 using this function.

Not at all. The part you refer to is just to check that we do have a
number on this line. The set of characters it will read is [0-9+-]
and only one of them. If this succeeds, the code puts it back and
reads the number using fscanf. The point is to ensure that there is
number there to be found. Presumably this was important (I can't
remember the problem definition).

I could have chained the sequence of things that must go right using
&& rather than testing for things that could go wrong using joined
using ||. I.e. I could have written:

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);
}
When I read the code back in the
AFL thread I thought it was an alternative to writing fscanf in main
()!

It is. It just does a bit more checking.
 
J

James Kuyper

jameskuyper said:
And that would obviously imply that I can read only positive integers
from 1-9 and 0 using this function.

Not quite. First of all, it's the format specifier "%1[0-9+-]" that
imposes such a restriction. The fscanf() function itself has no such
restriction. ...

Sorry - Ben's response made me realize that you were referring to
read_num(), not fscanf(), when you said "this function". That was
because fscanf() was the only function mentioned by name in that
message, and I didn't bother to look backwards to the previous messages.
I'm sorry for any confusion I may have caused by misinterpreting that
statement.
 
A

Albert

Here's a really easy problem:

Bugs

Input File: bugsin.txt
Output File: bugsout.txt
Time Limit: 1 second

You do not like bugs. This is unfortunate, since your town has been
taken over by a plague of small insects. Thankfully these insects only
come out every seven years — they appear all over town, make very loud
noises for a few months, lay their eggs and then disappear, until a
new generation of insects emerges from the eggs seven years later and
the cycle starts over again.

It is well known that the insects only emerge during years that are
exact multiples of seven. For instance, they appeared in the years
1995 and 2002 (since 1995 = 7 x 285 and 2002 = 7 x 286). You do a
quick calculation and deduce that they will reappear in the year 2009.
Flipping your calendar several years forward, you start making your
plans for a very long holiday.

You must write a program to help you and your townsfolk plan ahead for
the appearance of these insects in years to come. Your program must
read in a year and output the first year after that in which the
insects will emerge.
Input

The input will consist of a single line containing a single integer.
This integer will represent a year, and will be between 1 and 10,000
inclusive.
Output

Your output should consist of a single line containing a single
integer. This integer must be the first year after the input year in
which the insects will reappear.
Sample Input 1

2004

Sample Output 1

2009

Sample Input 2

1991

Sample Output 2

1995

Scoring

The score for each input file will be 100% if the correct year is
written to the output file and 0% otherwise.

If I may copy Ben's supplied number-input function, this is my new
code:

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

int main()
{
FILE *in = fopen("bugsin.txt", "r");
FILE* out = fopen("bugsout.txt", "w");
int input, nextyear;

input = read_num(in);
nextyear = input + 7 - input % 7;

fprintf(out, "%d\n", nextyear);
fclose(in);
fclose(out);

return 0;
}

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

Don't worry: it scored 100% on all test cases.
Do you think this is good enough for Richard Heathfield (in reference
to his posts on the AFL thread)?

TIA
Albert
 

Ask a Question

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.

Ask a Question

Members online

Forum statistics

Threads
473,771
Messages
2,569,587
Members
45,097
Latest member
RayE496148
Top