Strtol vs sscanf

W

whisper

Hello: I am trying to write code to read in a bunch of lines from
stdin,
each containing a (variable) number of integers and writing each
integer
in a separate line to stdout.

I came up the code below.

I could not get sscanf to work because it does not increment its
position
on conversion (like scanf does). Is there some workaround for it ?

Please criticize the code below: I have a feeling I am missing a simple
and elegant solution, since the problem is so simple itself.

Moreover, there is a *bug* in the code: what happens when I have a 0 as
a number ?
Reading will stop then. How do I get around that ?

Are my malloc() error statements correct ? In general, is it not better
to state in
the malloc error's before exiting which variables failed to get
malloc'ed ?


#include <stdio.h>
#define MAX 80

int main() {
int i, n;
char *line, **errline, *lineA, *lineB;

line=malloc(MAX*sizeof(char));
errline=malloc(sizeof(*errline));

if ((line == NULL) || (errline==NULL)) {
fprintf(stderr, "Could not allocate memory");
exit(-1);
}

while (fgets(line,MAX, stdin)!=NULL) {
lineA=line;
while ((n=(int)strtol(lineA, errline, 0))!=0) {
printf("%d \n", n);
lineA=*errline;
// printf("Line is now at %s \n",lineA);
}
}
}
 
E

Eric Sosman

whisper said:
Hello: I am trying to write code to read in a bunch of lines from
stdin,
each containing a (variable) number of integers and writing each
integer
in a separate line to stdout.

I came up the code below.

I could not get sscanf to work because it does not increment its
position
on conversion (like scanf does). Is there some workaround for it ?

One possibility is to use the "%n" specifier in the
sscanf() format string. This tells you how many characters
the sscanf() operation "swallowed," which you can use to
get the next sscanf() to begin its scan after the already-
used characters.
Please criticize the code below: I have a feeling I am missing a simple
and elegant solution, since the problem is so simple itself.

You are certainly working *much* too hard. I see no
reason to use malloc() at all for this task; every dynamic
allocation requires a pointer, but it does not follow that
every pointer requires a dynamic allocation!
Moreover, there is a *bug* in the code: what happens when I have a 0 as
a number ?
Reading will stop then. How do I get around that ?

sscanf() will detect the "no more data" condition and
report it "out of band," independently of the converted
number, so you need not reserve any special value. strtol()
sets a pointer (`errline' in your code) to the first byte
after converted material, or to the start of the string if
nothing could be converted; use that pointer to discover
when you've reached the end of the line.
Are my malloc() error statements correct ? In general, is it not better
to state in
the malloc error's before exiting which variables failed to get
malloc'ed ?

Halting the program on a malloc() failure is reasonable
for some programs, unreasonable for others; there is no one
error-handling strategy that works in all cases. exit(-1)
is a dubious practice, since -1 will have different meanings
on different C implementations; it's better to use either
EXIT_SUCCESS or EXIT_FAILURE (both from <stdlib.h>, which
you should have included anyhow since that's where malloc()
and strtol() are declared -- in fact, your compiler ought to
have given a diagnostic for this use of malloc() without a
declaration).

Printing out the names of internal variables involved
in a failure condition may be useful to you the programmer
(it may help your debugging), but is spectacularly unhelpful
to an end user who doesn't have access to your source code.
If your newsreader told you "Could not allocate memory for
buffer_3 in function filter_redo_delegate()", would you be
able to make any sense of it?
#include <stdio.h>
#define MAX 80

int main() {
int i, n;
char *line, **errline, *lineA, *lineB;

line=malloc(MAX*sizeof(char));
errline=malloc(sizeof(*errline));

if ((line == NULL) || (errline==NULL)) {
fprintf(stderr, "Could not allocate memory");
exit(-1);
}

while (fgets(line,MAX, stdin)!=NULL) {
lineA=line;
while ((n=(int)strtol(lineA, errline, 0))!=0) {
printf("%d \n", n);
lineA=*errline;
// printf("Line is now at %s \n",lineA);
}
}
}

Here's a considerably simpler version, using no dynamic
memory allocation at all:

#include <stdio.h>
#include <stdlib.h>
#define MAX 80

int main(void) {
char line[MAX];

while (fgets(line, sizeof line, stdin) != NULL) {
char *here, *next;
for (here = line; ; here = next) {
long value;
value = strtol(here, &next, 0);
if (next == here)
break;
printf ("%ld\n", value);
}
}

return EXIT_SUCCESS;
}

With not much more effort you could detect garbage inputs
like "123XYZ" and "99999999999999999999999999999999999999".
 
F

Fred L. Kleinschmidt

whisper said:
Hello: I am trying to write code to read in a bunch of lines from
stdin,
each containing a (variable) number of integers and writing each
integer
in a separate line to stdout.

I came up the code below.

I could not get sscanf to work because it does not increment its
position
on conversion (like scanf does). Is there some workaround for it ?

Please criticize the code below: I have a feeling I am missing a simple
and elegant solution, since the problem is so simple itself.

Moreover, there is a *bug* in the code: what happens when I have a 0 as
a number ?
Reading will stop then. How do I get around that ?

Are my malloc() error statements correct ? In general, is it not better
to state in
the malloc error's before exiting which variables failed to get
malloc'ed ?

#include <stdio.h>
#define MAX 80

int main() {
int i, n;
char *line, **errline, *lineA, *lineB;

line=malloc(MAX*sizeof(char));
errline=malloc(sizeof(*errline));

if ((line == NULL) || (errline==NULL)) {
fprintf(stderr, "Could not allocate memory");
exit(-1);
}

while (fgets(line,MAX, stdin)!=NULL) {
lineA=line;
while ((n=(int)strtol(lineA, errline, 0))!=0) {

This is not how you use strtol - you seem to be confused about how to
use the second argument. The proper way is:

char *ptr;
long int n;
char *line;

/* assume 'line' has been filled with a null-terminated string */

n = strtol( line, &ptr, 0 );

At this point, ptr points to the first character in 'line' that was not
read as part of the number that translates into 'n'.
 
M

Michael Mair

whisper said:
Hello: I am trying to write code to read in a bunch of lines from
stdin,
each containing a (variable) number of integers and writing each
integer
in a separate line to stdout.

Question: Do you want to know whether a line break happened between
numbers?
I came up the code below.

I could not get sscanf to work because it does not increment its
position
on conversion (like scanf does). Is there some workaround for it ?

Please criticize the code below: I have a feeling I am missing a simple
and elegant solution, since the problem is so simple itself.

Moreover, there is a *bug* in the code: what happens when I have a 0 as
a number ?
Reading will stop then. How do I get around that ?

Are my malloc() error statements correct ? In general, is it not better
to state in
the malloc error's before exiting which variables failed to get
malloc'ed ?


#include <stdio.h>
#include said:
#define MAX 80
Rather call that LINE_MAX or some such: It is clearer.
int main() {
int i, n; i is unused
char *line, **errline, *lineA, *lineB; lineB is unused

line=malloc(MAX*sizeof(char));
sizeof(char) is _always_ 1. Use MAX*sizeof *line or just MAX.
errline=malloc(sizeof(*errline));

if ((line == NULL) || (errline==NULL)) {
fprintf(stderr, "Could not allocate memory");
Insert
free(line);
here: If line==NULL, free(line) does nothing, otherwise
the malloc()ed memory gets freed.
Better yet: Check immediately after trying to malloc().
Apart from that: Instead of char **errline, use char *endptr
and pass &endptr -- saves one malloc() and free() and is IMO
less opaque.
exit(-1); exit(EXIT_FAILURE);
}

while (fgets(line,MAX, stdin)!=NULL) {
lineA=line;
while ((n=(int)strtol(lineA, errline, 0))!=0) {
you are losing information here, moreover, if LONG_MAX>INT_MAX
(or LONG_MIN<INT_MIN), you are in trouble, having invoked undefined
behavior.
printf("%d \n", n);
lineA=*errline;
// printf("Line is now at %s \n",lineA);
}
}
Do not forget to free()!!!
free(line);
free(errline);

Your program stops (without errors encountered) only at
reading of EOF. Is that intended?
Moreover: Your use of strtol is rather strange.
I will comment further on that later on.

If you want to use a scanf()-like function, do not use sscanf(),
rather use fscanf() for an arbitrary number of numbers per line:

#include <stdio.h>

int main (void)
{
int num;

while (fscanf(stdin," %d",&num)==1)
printf("%d\n", num);

return 0;
}

Back to strol:
I would do it like that

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

#define LINE_MAX 80

int main (void) {
int num;
char *line, *nptr, *endptr;

line = malloc(LINE_MAX*sizeof *line);
if ( line == NULL ) {
fprintf(stderr, "Could not allocate memory");
exit(EXIT_FAILURE);
}

while (fgets(line,LINE_MAX, stdin) != NULL) {
int err=0;
nptr=line;
while (!err) {
long tmp = strtol(nptr, &endptr, 0);

switch (tmp) {
case LONG_MIN:
case LONG_MAX:
/* Check for range error */
errno = 0;
tmp = strtol(nptr, &endptr, 0);
if (errno==ERANGE)
err = 1;
break;
case 0:
if (nptr==endptr) {
err = 1;
break;
}
default:
if (tmp>INT_MAX || tmp<INT_MIN) {
err = 1;
break;
}

printf("%d\n",num=(int)tmp);
break;
}
nptr = endptr;
}
}

free(line);
return 0;
}


Note, however, that fgets() is not safe. Have a look at the
FAQ or google for one of the various Pop's Device vs fgets()
discussions.
For the future: Read the manpage of functions you are using
(google for "man strtol", e.g.)

Cheers
Michael
 

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,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top