Checking for valid number syntax

C

Count Dracula

The following three functions check whether a given string
represents a valid number. They require that the entire string
represent
a number, unlike strtod which accepts strings like
"99rQF" and returns 99. Is there a way to make strtod and friends
behave like the functions below? Is there some room for improvement
of the functions below from the point of view of speed of execution?
The code below should be compilable.

Thanks,

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

int validinteger(const char* s)
{
/* input string s may have leading or trailing blanks */
const int false = 0;
size_t n = 0, N = strlen(s);
int state = 0;
int c;
while (n < N)
{
c = s[n];
switch (state)
{
case 0:
if (isdigit(c))
state = 1;
else if (c == '-' || c == '+')
state = 2;
else if (!isspace(c))
return false;
break;
case 1:
if (isspace(c))
state = 3;
else if (!isdigit(c))
return false;
break;
case 2:
if (isdigit(c))
state = 4;
else
return false;
break;
case 3:
if (!isspace(c))
return false;
break;
case 4:
if (isspace(c))
state = 3;
else if (!isdigit(c))
return false;
break;
}
++n;
}
return state == 1 || state == 3 || state == 4;
}

int validfloat(const char* s)
{
/* input string s may have leading or trailing blanks */
const int false = 0;
size_t n = 0, N = strlen(s);
int state = 0;
int c;
while (n < N)
{
c = s[n];
switch (state)
{
case 0:
if (isdigit(c))
state = 1;
else if (c == '-' || c == '+')
state = 2;
else if (c == '.')
state = 5;
else if (!isspace(c))
return false;
break;
case 1:
if (isspace(c))
state = 6;
else if (c == '.')
state = 3;
else if (!isdigit(c))
return false;
break;
case 2:
if (isdigit(c))
state = 1;
else if (c == '.')
state = 5;
else
return false;
break;
case 3:
if (isdigit(c))
state = 4;
else if (isspace(c))
state = 6;
else
return false;
break;
case 4:
if (isspace(c))
state = 6;
else if (!isdigit(c))
return false;
break;
case 5:
if (isdigit(c))
state = 4;
else
return false;
break;
case 6:
if (!isspace(c))
return false;
break;
}
++n;
}
return state == 1 || state == 3 || state == 4 || state == 6;
}

int validreal(const char* s)
{
/* input string may have leading or trailing blanks */
int k, c;
char buffer1[64], buffer2[64];
const char *n;
char* p = strchr(s, 'e');
char* q = NULL;
if (p == NULL)
q = strchr(s, 'E');

if (p == NULL && q == NULL)
return validfloat(s);
if (p != NULL)
c = *p;
else if (q != NULL)
c = *q;

n = &s[0];
k = 0;
while (*n != c)
{
buffer1[k] = *n;
++k;
++n;
}
buffer1[k] = '\0';
++n;
k = 0;
while (*n != '\0')
{
buffer2[k] = *n;
++k;
++n;
}
buffer2[k] = '\0';
return validfloat(buffer1) && validinteger(buffer2);
}

int main (int argc, char** argv)
{
int n;
int x;
for (n = 1; n < argc; ++n)
{
x = validreal(argv[n]);
printf("%12s %10d \n", argv[n], x);
}
return 0;
}

--
 
M

Michal Nazarewicz

Count Dracula said:
The following three functions check whether a given string
represents a valid number. They require that the entire string
represent
a number, unlike strtod which accepts strings like
"99rQF" and returns 99.

You can always do the following:

#v+
#include <stdlib.h>
/* ... *

int validint(const char *str, long *val) {
if (!str || !*str) {
return 0;
} else {
char *end;
long v = strtol(str, &end, 0);
if (*end) {
return 0;
} else {
*val = v;
return 1;
}
}
}

int validdouble(const char *str, double *val) {
if (!str || !*str) {
return 0;
} else {
char *end;
double v = strtod(str, &end);
if (*end) {
return 0;
} else {
*val = v;
return 1;
}
}
}
 
F

Fred Kleinschmidt

Michal Nazarewicz said:
You can always do the following:

#v+
#include <stdlib.h>
/* ... *

int validint(const char *str, long *val) {
if (!str || !*str) {
return 0;
} else {
char *end;
long v = strtol(str, &end, 0);
if (*end) {
return 0;
} else {
*val = v;
return 1;
}
}
}

int validdouble(const char *str, double *val) {
if (!str || !*str) {
return 0;
} else {
char *end;
double v = strtod(str, &end);
if (*end) {
return 0;
} else {
*val = v;
return 1;
}
}
}

Why not return:
-1 on success,
0 for null or empty string
end-str for bad format

This gives the index of the offending character.
--
Fred L. Kleinschmidt
Boeing Associate Technical Fellow
Technical Architect, Software Reuse Project
..
 
M

Michal Nazarewicz

[code removed]

Fred Kleinschmidt said:
Why not return:
-1 on success,
0 for null or empty string
end-str for bad format

This gives the index of the offending character.

But of course - if you'd like to. :) My point is that there's no need
to write those functions from stretch but strtol() and strtod() can be
used.

#v+
#include <stdlib.h>

long parseint(const char *str, long *val) {
if (!str || !*str) {
return 0;
} else {
char *end;
long v = strtol(str, &end, 0);
if (*end) return end - str;
if (val) *val = v;
return -1;
}
}
#v-
 
J

Jorgen Grahn

[code removed]

Fred Kleinschmidt said:
Why not return:
-1 on success,
0 for null or empty string
end-str for bad format

This gives the index of the offending character.

But of course - if you'd like to. :) My point is that there's no need
to write those functions from stretch but strtol() and strtod() can be
used.

Also worth to keep in mind: both strtol() and strtod() accept a much wider
syntax than the original poster's code (hexadecimal, "NaN", etc etc). And he
accepted one thing those functions don't: leading/trailing whitespace.

We had a long discussion about this recently in
se.dator.programmering.diverse, but of course that's in Swedish ...

But yes, he's probably better off with the standard functions.

/Jorgen
 
R

Random832

2006-12-21 said:
The following three functions check whether a given string
represents a valid number. They require that the entire string
represent
a number, unlike strtod which accepts strings like
"99rQF" and returns 99.
You can always do the following:
[code removed]

Fred Kleinschmidt said:
Why not return:
-1 on success,
0 for null or empty string
end-str for bad format

This gives the index of the offending character.

But of course - if you'd like to. :) My point is that there's no need
to write those functions from stretch but strtol() and strtod() can be
used.

Also worth to keep in mind: both strtol() and strtod() accept a much wider
syntax than the original poster's code (hexadecimal, "NaN", etc etc). And he
accepted one thing those functions don't: leading/trailing whitespace.

In what way do those functions reject trailing whitespace in the sense
that they can be said to accept trailing "rQF"?
 
B

Ben Pfaff

Jorgen Grahn said:
Also worth to keep in mind: both strtol() and strtod() accept a much wider
syntax than the original poster's code (hexadecimal, "NaN", etc etc). And he
accepted one thing those functions don't: leading/trailing whitespace.

strtol and strtod can both successfully convert strings that
contain leading and trailing white-space characters.
 
J

Jalapeno

Ben Pfaff wrote:
(in his sig)
int main(void){char p[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.\
\n",*q="kl BIcNBFr.NKEzjwCIxNJC";int i=sizeof p/2;char *strchr();int putchar(\
);while(*q){i+=strchr(p,*q++)-p;if(i>=(int)sizeof p)i-=sizeof p-1;putchar(p\
);}return 0;}


Is that an ascii thing? I get

KvTUbCPQVKIVeifNHJRMZhj

as output on my system (ebcdic).
 
B

Ben Pfaff

Jalapeno said:
Ben Pfaff wrote:
(in his sig)
int main(void){char p[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.\
\n",*q="kl BIcNBFr.NKEzjwCIxNJC";int i=sizeof p/2;char *strchr();int putchar(\
);while(*q){i+=strchr(p,*q++)-p;if(i>=(int)sizeof p)i-=sizeof p-1;putchar(p\
);}return 0;}


Is that an ascii thing?
No.

I get

KvTUbCPQVKIVeifNHJRMZhj

as output on my system (ebcdic).


Perhaps you cut and pasted it incorrectly, e.g. by omitting the
space at the beginning of the second line.
 
J

Jorgen Grahn

strtol and strtod can both successfully convert strings that
contain leading and trailing white-space characters.

Oops, you're right, both of you. Although you would have to do extra work
to see that " 42 " is valid, but " 42 years old" isn't.

My main point was the first though: that he rejects things that strod()
accepts. To some extent, that applies to strtol() too.

/Jorgen
 

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

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top