Reputation: 31
I'm trying to write a program that takes in two ints as command line arguments. The ints both need to be greater than 0. I understand that I need to convert from char but I have only ever done that using atoi which I now know that I shouldn't do. I've seen people use sstreams and strtol but I'm not sure how those would work in this case. What is the best way to accomplish this?
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int N = 7;
const int M = 8;//N is number of lines, M number of values
//--------------
//-----Main-----
//--------------
int main(int argc, char* argv[])
{
if((argc != 0) && (argv[0] != NULL) && (argv[1] != NULL))
{
N = argv[0];
M = argv[1];
}
else
{
cout << "Invalid or no command line arguments found. Defaulting to N=7 M=8.\n\n" << endl;
}
//Blah blah blah code here
return 0;
}
Upvotes: 2
Views: 4021
Reputation: 15683
In C++11 there's stoi
, stol
, stoll
for this: http://en.cppreference.com/w/cpp/string/basic_string/stol
Those throw invalid_argument
or out_of_range
exceptions if the string isn't in the right format.
There's nothing particularly wrong about using atoi
, except it doesn't have a mechanism to report exceptions because it's a C function. So you only have the return value - the problem is all return values of atoi
are valid values, so there's no way to differentiate between the return value of 0 as the correct parsing of "0" or the failure of parsing. Also, atoi doesn't do any checks for whether the value is outside the available value range. The first problem is easy to fix by doing the check yourself, the second is more difficult because it involves actually parsing the string - which kind of defeats the point of using an external function in the first place.
You can use istringstream
like this:
Pre-C++11:
int val;
std::istringstream iss(arg[i]);
iss >> val;
if (iss.fail()) {
//something went wrong
} else {
//apparently it worked
}
C++11:
int val;
std::istringstream iss(arg[i]);
iss >> val;
if(iss.fail()) {
if(!value) {
//wrong number format
} else if(value == std::numeric_limits<int>::max() ||
value == std::numeric_limits<int>::min()
{
//number too large or too small
}
} else {
//apparently it worked
}
The difference is that pre C++11, only format errors were detected (according to standard), also it wouldn't overwrite the value on error. In C++11, values are overwritten by either 0 if it's a format error or max/min if the number is too large or too small to fit into the type. Both set the fail flag on the stream to indicate errors.
Upvotes: 1
Reputation: 22157
First, you can't use const
qualifier for M and N, since you will change their value:
int N = 7;
int M = 8;//N is number of lines, M number of values
Second, you don't need to check for (argv[0] != NULL) && (argv[1] != NULL)
, just check if argc
(argument count) is greater or equal to 3:
if(argc >= 3)
Then you need to convert this into integers. If you don't want to use atoi
, and if you don't have C++11 compiler you should use C++'s stringstream
or C's strtol
stringstream ss;
int temp;
ss << argv[1]; // Put string into stringstream
ss >> temp; // Get integer from stringstream
// Check for the error:
if(!ss.fail())
{
M = temp;
}
// Repeat
ss.clear(); // Clear the current content!
ss << argv[2]; // Put string into stringstream
ss >> temp; // Get integer from stringstream
// Check for the error:
if(!ss.fail())
{
N = temp;
}
so, whole code will look like this:
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <sstream>
using namespace std;
int N = 7;
int M = 8;//N is number of lines, M number of values
//--------------
//-----Main-----
//--------------
int main(int argc, char* argv[])
{
if(argc >= 3)
{
stringstream ss;
int temp;
ss << argv[1]; // Put char into stringstream
ss >> temp; // Get integer from stringstream
// Check for the error:
if(!ss.fail())
{
M = temp;
}
// Repeat
// empty
ss.clear();
ss << argv[2]; // Put char into stringstream
ss >> temp; // Get integer from stringstream
// Check for the error:
if(!ss.fail())
{
N = temp;
}
cout << M << " " << N;
}
else
{
cout << "Invalid or no command line arguments found. Defaulting to N=7 M=8.\n\n" <<
endl;
}
//Blah blah blah code here
return 0;
}
Also, C header files include with c
prefix, not with .h
suffix (<cstdio>
instead of <stdio.h>
)
Upvotes: 0
Reputation: 490148
In this specific case, atoi
will work fine. The problem with atoi
is that you can't differentiate between its returning 0
to signify an error of some sort, and its returning 0
to indicate that the input was 0
.
In your case, however, a valid input must be greater than 0. You don't care whether the input was 0
or something else that couldn't be converted. Either way you're doing to set it to the default value.
As such, I'd do something like:
int convert(char *input, int default) {
int x = atoi(input);
return x==0 ? default : x;
}
if (argc > 1)
N = convert(argv[1], 7);
if (argc > 2)
M = convert(argv[2], 8);
Note that argv[0]
traditionally holds the name of the program being invoked. Arguments passed on the command line are received as argv[1]
through argv[argc-1]
.
Upvotes: 0