Program in c . My goal for this assignment is to make sure the user enter only one argument in command line and that argument has to contain digits only and greater than 3. Otherwise, print error. For example:
$ ./a.out 4
Successful!
$ ./a.out abc
Must be a numeric string grater than 3
$ ./a.out 4 abc
Must be one argument only
$ ./a.out 2
Must be a numeric string greater than 3
$ ./a.out 2ab
Must be a numeric string
This is my code so far to handle accepting one argument and greater than 3. I don't know to handle the numeric string part.
int main(int argc, char **argv){
if (argc == 2){
int num = atoi(argv[1];
if (num >3){
cout << "Successful" << endl;
else{
cout <<"Must be a numeric string greater than 3"<< endl;
}
else{
cout << "Must be one argument" << endl;
}
return 0;
}
I have tried this and got segmentation error.
int main(int argv, char **argv){
if (argc == 2){
int num = atoi(argv[1];
int i;
if (num >=3 && isdigit(argv[1][i]){
cout << "Successful" << endl;
else{
cout << "Must be a numeric string greater than 3"<<endl;
}
else{
cout << "Must be one argument" << endl;
}
return 0;
}
CodePudding user response:
You could use std::strtol instead of atoi
; this way you can check that the parsing stopped at the end of that string.
CodePudding user response:
here is code that compiles and does not crash but doesn't produce the right answer. It doesn't work because you are only looking at the first char of argv[1]. Why did it crash, becuase you indexed using i
which was untitialized
#include <iostream>
#include<ctype.h>
int main(int argc, char** argv) {
if (argc == 2) {
int num = atoi(argv[1]);
int i = 0;// <<<<========================================
if (num >= 3 && isdigit(argv[1][i])) {
std::cout << "Successful" << std::endl;
}
else {
std::cout << "Invalid" << std::endl;
}
}
else {
std::cout << "Must be one argument" << std::endl;
}
return 0;
}
to make it do the right thing you need to loop over all argv and inside that loop you must loop over all chars in argv[i]
note that I removed the using namespace
- see here Why is "using namespace std;" considered bad practice?
CodePudding user response:
Your text and your code are at odds with each other. Is 3 acceptable or not? My code below assumes it is not based on what you wrote.
The big thing here is that atoi()
lacks the ability to tell you if the whole string was processed or not. You want to use what C provides from <string>
, std::stoi()
or the long
or long long
variations depending on what you expect a reasonable range of inputs to be.
std::stoi()
has a second output parameter that tells you how many characters were processed. What you then need to check is if the number of characters processed is the length of the string. If not, some non-numeric charcters were entered.
Example code below.
#include <cstring>
#include <iostream>
#include <string>
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "USAGE: ./a.out ARG\n";
return 1;
}
std::size_t loc;
int val;
// The try block is necessary because std::stoi() throws if the resulting
// number cannot be contained in the type, and if processing fails on the
// first character.
try {
val = std::stoi(argv[1], &loc);
} catch (...) {
std::cerr << "INVALID PARAMETER\n";
return 2;
}
if (loc != std::strlen(argv[1])) {
std::cerr << "INPUT MUST BE FULLY NUMERIC\n";
return 3;
}
if (val <= 3) {
std::cerr << "Parameter must be > 3\n";
return 4;
}
std::cout << "Parameter: " << val << '\n';
}
Test runs:
~/tmp
❯ ./a.out
USAGE: ./a.out ARG
~/tmp
❯ ./a.out 1
Parameter must be > 3
~/tmp
❯ ./a.out 4
Parameter: 4
~/tmp
❯ ./a.out 2ab
INPUT MUST BE FULLY NUMERIC
~/tmp
❯ ./a.out ab
INVALID PARAMETER
CodePudding user response:
Other answers are good and correct. My goto for stuff like this is to use a stringstream.
#include <optional>
#include <sstream>
#include <string>
template <typename T>
auto string_to( const std::string & s )
{
T value;
return (std::istringstream( s ) >> value >> std::ws).eof()
? value
: std::optional <T> {};
}
It is easy enough to use:
#include <iostream>
int error( const char * message )
{
std::cerr << message << "\n";
return 1;
}
int main( int argc, char ** argv )
{
if (argc != 2) return error( "Must be one argument only" );
auto n = string_to<int>( argv[1] );
if (!n) return error( "Must be a numeric string" );
if (n <= 3) then error( "Must be a numeric string grater than 3" );
std::cout << "Successful!\n";
}
The basic principle behind all these answers is you must try to convert it to an integer to see if it is, in fact, an integer.
I like my little utility function (string_to<type>()
) because it does all the dirty work correctly: it attempts to convert the value, meaning there may be whitespace before the value but nothing else. It then reads any remaining whitespace and checks for EOF (meaning nothing but whitespace may follow the value). Only if all the checks pass do you get a value back.
I like std::optional
because it is exactly for these kinds of things — either you have a value or you do not. You could throw
instead, or return a default value, or whatever works. Heck, you could inline it in your main function:
int main(...)
{
...
int value;
if (!(std::istringstream( argv[1] ) >> value >> std::ws).eof())
error( ... );
That’s ugly though, and not very descriptive. I prefer to put things in little helper functions (which the compiler may very well inline) with nice, descriptive names. Like that error function I used there: it tells you exactly what is going on just by reading the code.