Forget about ASCII code checks, use isdigit or isnumber (see man isnumber). The first function checks whether the character is 0–9, the second one also accepts various other number characters depending on the current locale.
There may even be better functions to do the check – the important lesson is that this is a bit more complex than it looks, because the precise definition of a “number string” depends on the particular locale and the string encoding.
Answer from zoul on Stack OverflowForget about ASCII code checks, use isdigit or isnumber (see man isnumber). The first function checks whether the character is 0–9, the second one also accepts various other number characters depending on the current locale.
There may even be better functions to do the check – the important lesson is that this is a bit more complex than it looks, because the precise definition of a “number string” depends on the particular locale and the string encoding.
Copy if(tmp[j] >= '0' && tmp[j] <= '9') // should do the trick
Videos
I am at "Caesar" with the encryption code and I just need to turn the input into an integer and print an error messsage if it is not an integer. I can't seem to find a solution online including the right libraries for the Github cs50 VS (like iostream dont work?) and I can't find any clues in the lecture notes on this! Where do I go?
Have a look at strtol(), it can tell you about invalid parts of the string by pointer return.
And beware of enthusiastic example code.. see the man page for comprehensive error-handling.
Maybe I'll get flamed for not using strtol or similar libc functions, but reasoning about this problem is not that hard:
#include <stdbool.h> // if using C99... for C++ leave this out.
#include <ctype.h>
bool is_valid_int(const char *str)
{
// Handle negative numbers.
//
if (*str == '-')
++str;
// Handle empty string or just "-".
//
if (!*str)
return false;
// Check for non-digit chars in the rest of the stirng.
//
while (*str)
{
if (!isdigit(*str))
return false;
else
++str;
}
return true;
}
[NB: I might have otherwise done isdigit(*str++) instead of the else to keep it shorter but my recollection is that the standards say it's possible that isdigit is a macro.]
I guess one limitation is that this does not return false if the number in the string won't fit in an integer. That may or may not matter to you.
num will always contain an integer because it's an int. The real problem with your code is that you don't check the scanf return value. scanf returns the number of successfully read items, so in this case it must return 1 for valid values. If not, an invalid integer value was entered and the num variable did probably not get changed (i.e. still has an arbitrary value because you didn't initialize it).
As of your comment, you only want to allow the user to enter an integer followed by the enter key. Unfortunately, this can't be simply achieved by scanf("%d\n"), but here's a trick to do it:
Copyint num;
char term;
if(scanf("%d%c", &num, &term) != 2 || term != '\n')
printf("failure\n");
else
printf("valid integer followed by enter key\n");
You need to read your input as a string first, then parse the string to see if it contains valid numeric characters. If it does then you can convert it to an integer.
Copychar s[MAX_LINE];
valid = FALSE;
fgets(s, sizeof(s), stdin);
len = strlen(s);
while (len > 0 && isspace(s[len - 1]))
len--; // strip trailing newline or other white space
if (len > 0)
{
valid = TRUE;
for (i = 0; i < len; ++i)
{
if (!isdigit(s[i]))
{
valid = FALSE;
break;
}
}
}
Parameters passed by command line are always strings, if you want to check if this string can be converted to integer you can use strtol:
char *ptr = argv[1];
long num;
num = strtol(ptr, &ptr, 10);
if (*ptr == '\0')
/* arg is a number */
else
/* arg is NOT a number */
You can call isdigit() on each character of the string, and if it's true for all characters you have an integer, otherwise it's some alphanumeric string.
You can also call strtol to parse the string as an integer. The second argument returns a pointer to the first non-numeric character in the string. If it points to the first character, it's not an integer. If it points to the end, it's an integer. If it points somewhere in the middle, it's an integer followed by a sequence of non-numeric characters.
The most efficient way would be just to iterate over the string until you find a non-digit character. If there are any non-digit characters, you can consider the string not a number.
bool is_number(const std::string& s)
{
std::string::const_iterator it = s.begin();
while (it != s.end() && std::isdigit(*it)) ++it;
return !s.empty() && it == s.end();
}
Or if you want to do it the C++11 way:
bool is_number(const std::string& s)
{
return !s.empty() && std::find_if(s.begin(),
s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end();
}
As pointed out in the comments below, this only works for positive integers. If you need to detect negative integers or fractions, you should go with a more robust library-based solution. Although, adding support for negative integers is pretty trivial.
Why reinvent the wheel? The C standard library (available in C++ as well) has a function that does exactly this:
char* p;
long converted = strtol(s, &p, 10);
if (*p) {
// conversion failed because the input wasn't a number
}
else {
// use converted
}
If you want to handle fractions or scientific notation, go with strtod instead (you'll get a double result).
If you want to allow hexadecimal and octal constants in C/C++ style ("0xABC"), then make the last parameter 0 instead.
Your function then can be written as
bool isParam(string line)
{
char* p;
strtol(line.c_str(), &p, 10);
return *p == 0;
}
According with the man page of strtol. You must define your function such as:
bool isNumeric(const std::string& str) {
char *end;
long val = std::strtol(str.c_str(), &end, 10);
if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) {
// if the converted value would fall out of the range of the result type.
return false;
}
if (end == str) {
// No digits were found.
return false;
}
// check if the string was fully processed.
return *end == '\0';
}
In C++11, I prefer to use std::stol instead of std::strtol, such as:
bool isNumeric(const std::string& str) {
try {
size_t sz;
std::stol(str, &sz);
return sz == str.size();
} catch (const std::invalid_argument&) {
// if no conversion could be performed.
return false;
} catch (const std::out_of_range&) {
// if the converted value would fall out of the range of the result type.
return false;
}
}
std::stol calls std::strtol, but you works directly with std::string and the code is simplified.
strtol stops on the first non digit
but if you read the man page http://man7.org/linux/man-pages/man3/strtol.3.html you can see
If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, strtol() stores the original value of nptr in *endptr (and returns 0). In particular, if *nptr is not '\0' but **endptr is '\0' on return, the entire string is valid.
ie
string testString = "ANYTHING";
cout << "testString = " << testString << endl;
char *endptr;
int testInt = strtol(testString.c_str(),&endptr,0);
if(**endptr)
cout << "bad input";