Simple Calculator using command line argument C++ - c++

I'm new to C++. I'm writing a simple calculator using command line. The command line should have this format:
programname firstNumber operator secondNumber
Here what I got so far:
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
if (argc != 3)
{
cerr << "Usage: " << argv[0] << endl;
exit(0);
}
else
{
int firstNumber = atoi(argv[1]);
char theOperator = atoi(argv[2]);
int secondNumber = atoi(argv[3]);
switch (theOperator)
{
case'+':
{
cout << "The answer is " << firstNumber + secondNumber << endl;
break;
}
case '-':
{
cout << "The answer is " << firstNumber - secondNumber << endl;
break;
}
case '*':
{
cout << "The answer is " << firstNumber * secondNumber << endl;
break;
}
case '/':
{
if (secondNumber == 0)
{
cout << "Can not devide by a ZERO" << endl;
break;
}
else
{
cout << "The answer is " << firstNumber / secondNumber << endl;
break;
}
}
}
}
}
The program does not run. When I run it, it displays an appropriate usage message and end the program. Can anyone please help me?

Others have already given you the answer but you could have very easily figured this one out on your own. Just print what argc is at the point where you know the code is going into:
int main(int argc, char* argv[])
{
if (argc != 3)
{
cout << "argc is: " << argc << endl; // Debug output that you delete later
cerr << "Usage: " << argv[0] << endl;
exit(0);
}
else
And then come back with what argc is. When you find that argc is actually 4 and you want to know what is inside argc you should write some code to print it so that you can figure it out... Like this:
int main(int argc, char* argv[])
{
cout << "argc is: " << argc << endl; // Debug output that you delete later
for (int i = 0; i < argc; ++i)
{
// Print out all of the arguments since it's not working as you expect...
cout << "argv[" << i << "] = " << argv[i] << endl;
}
if (argc != 3)
{
cerr << "Usage: " << argv[0] << endl;
exit(0);
}
else
and you would have very quickly figured out what is wrong...
Please learn how to do this because it will save your but in the future and you won't have to wait for an answer here.
Additionally there is another error in your code.
Why on earth are you converting the + character from a string to an int?
else
{
int firstNumber = atoi(argv[1]);
char theOperator = atoi(argv[2]); // <<< WTF? Why?
int secondNumber = atoi(argv[3]);
switch (theOperator)
You probably want to get rid of the atoi part there and just go with:
char theOperator = argv[2][0]; // First character of the string
Provided that the second argument will always have only one letter... Which you might want to enforce/check. See strlen() and std::string and note that the type of argv[2] is char* (pointer to char).
I also recommend that you read How to debug small programs which is linked from the SO Howto-Ask Help Page. It may help a little. And no, I don't think your question is bad. Debugging small programs is a skill you'll need in the future if you intend to program so it will benefit you to learn it now.
Welcome to programming and C++ :)

The parameter argc also counts the program's name.
Try this:
if (argc != 4) // We expect 4 arguments: programname number operator number
{
cerr << "Usage: " << argv[0] << " <number> <operator> <number>" << endl;
exit(0);
}
In your code, running the program correctly (with all 3 parameters) displays the error message because argc equals 4.

If you type in
programname firstNumber operator secondNumber
You have 4 arguments, not 3.
argv[0] = programname
argv[1] = firstNumber
argv[2] = operator
argv[3] = secondNumber
Looks like your program is working correctly... at least as far as printing the usage message goes.
See also the other comments regarding your use of the operator argument.

Related

C++ std::logic_error within for loop using argv

I am new to C++ and I am trying to write a program to take in command line arguments and produce a .desktop file. I am trying to implement identification of the argv values but I keep getting a std::logic_error
My code is:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string>
using namespace std;
int main(int argc, char* argv[]) {
string name;
string comment;
for(int i = 1; i <= argc; i++) {
char* tmp[] = {argv[i]};
string param = *tmp;
string paramVal = argv[i+1];
if(param == "-h") {
cout << "-h Display this help dialogue" << endl;
cout << "-n Set entry name" << endl;
cout << "-c Set entry comment" << endl;
cout << "-e Set entry executable path" << endl;
cout << "-i Set entry icon" << endl;
break;
}
else if(param == "-n") {
name = paramVal;
i++;
continue;
}
else if(param == "-c") {
comment = paramVal;
i++;
continue;
}
else if(param == "-e") {
}
else if(param == "-i") {
}
else {
cout << "ERROR >>> Unrecognised parameter %s" << param << endl;
}
}
cout << "Name: %s\nComment: %s" << name << comment << endl;
return(0);
}
The program compiles fine (using g++) but when I try to run ./createDesktopIcon -n a -c b I get the following error
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
Aborted
Please help as it is very frustrating
Here are the problems I see:
i <= argc
You want to compare i < argc because the argv[argc] element in the array is actually one past the last element in the argv array.
Also, here:
string paramVal = argv[i+1];
This will access the array out of bounds as well.
You might want to look at getopt to do all of this for you.

C++ Trouble Modifying A String

So in this program I'm trying to go through word by word and make it only lowercase letters, no whitespace or anything else. However, my string "temp" isn't holding anything in it. Is it because of the way I'm trying to modify it? Maybe I should try using a char * instead? Sorry if this is a stupid question, I'm brand new to c++, but I've been trying to debug it for hours and can't find much searching for this.
#include <string>
#include <iostream>
#include <fstream>
#include <ctype.h>
using namespace std;
int main(int argc, char* argv[]) {
/*if (argc != 3) {
cout << "Error: wrong number of arguments." << endl;
}*/
ifstream infile(argv[1]);
//infile.open(argv[1]);
string content((std::istreambuf_iterator<char>(infile)),
(std::istreambuf_iterator<char>()));
string final;
string temp;
string distinct[5000];
int distinctnum[5000] = { 0 };
int numdist = 0;
int wordcount = 0;
int i = 0;
int j = 0;
int k = 0;
int isdistinct = 0;
int len = content.length();
//cout << "test 1" << endl;
cout << "length of string: " << len << endl;
cout << "content entered: " << content << endl;
while (i < len) {
temp.clear();
//cout << "test 2" << endl;
if (isalpha(content[i])) {
//cout << "test 3" << endl;
if (isupper(content[i])) {
//cout << "test 4" << endl;
temp[j] = tolower(content[i]);
++j;
}
else {
//cout << "test 5" << endl;
temp[j] = content[i];
++j;
}
}
else {
cout << temp << endl;
//cout << "test 6" << endl;
++wordcount;
final = final + temp;
j = 0;
for (k = 0;k < numdist;k++) {
//cout << "test 7" << endl;
if (distinct[k] == temp) {
++distinctnum[k];
isdistinct = 1;
break;
}
}
if (isdistinct == 0) {
//cout << "test 8" << endl;
distinct[numdist] = temp;
++numdist;
}
}
//cout << temp << endl;
++i;
}
cout << wordcount+1 << " words total." << endl << numdist << " distinct words." << endl;
cout << "New output: " << final << endl;
return 0;
}
You can't add to a string with operator[]. You can only modify what's already there. Since temp is created empty and routinely cleared, using [] is undefined. The string length is zero, so any indexing is out of bounds. There may be nothing there at all. Even if the program manages to survive this abuse, the string length is likely to still be zero, and operations on the string will result in nothing happening.
In keeping with what OP currently has, I see two easy options:
Treat the string the same way you would a std::vector and push_back
temp.push_back(tolower(content[i]));
or
Build up a std::stringstream
stream << tolower(content[i])
and convert the result into a string when finished
string temp = stream.str();
Either approach eliminates the need for a j counter as strings know how long they are.
However, OP can pull and endrun around this whole problem and use std::transform
std::transform(content.begin(), content.end(), content.begin(), ::tolower);
to convert the whole string in one shot and then concentrate on splitting the lower case string with substring. The colons in front of ::tolower are there to prevent confusion with other tolowers since proper namespacing of the standard library has been switched off with using namespace std;
Off topic, it looks like OP is performing a frequency count on words. Look into std::map<string, int> distinct;. You can reduce the gathering and comparison testing to
distinct[temp]++;

Handling Arguments in C++ not working

I am using this code:
int handleArgs(int argc, char *argv[]) {
if(argc <= 1)
{return 0;}
else
{ // If no arguments, terminate, otherwise: handle args
for(int i=1; i<argc; i++) {
if (argv[i] == "-a" || argv[i] == "--admin")
{ // If admin argument
char *pwd = argv[i+1]; // i + 1 b/c we want the next argument; the password
if(pwd == "1729" || pwd == "GabeN")
{ // Verify Password
cout << "Sorry, console feature unavailable.\n" << endl;// Will replace with console function when I get to it
}
else
{
cout << "Wrong/No passkey, bruh.\n" << endl;
} // If the password is wrong
}
else if (argv[i] == "-v" || argv[i] == "--version")
{ // If user asks for version info
cout << "You are running\nDev0.0.0" << endl; // Tell the user the version
}
else if (argv[i]==" -h" || argv[i]=="--help")
{
cout << "Usage: " << argv[0] << " -[switch] argument(s)\n";
cout << " -a, --admin Open console view. Requires password\n";
cout << " -v, --version Print version and exit\n";
cout << " -h, --help Print this message and exit\n" << endl;
}
else {
cout << "Is you dumb?\n '" << argv[0] << " --help' for help" << endl; // Insult the user
}
}
}
return 1;
}
However, every time I give it an argument, I receive the invalid argument message (the last else statement):
Is you dumb?
'main --help' for help
I'm new to C++, and I have no idea what I am doing (wrong). Could anyone provide me with some helpful insights? Thanks
--FracturedCode
argv is an array of C-Strings (char*). You are using == and comparing memory addresses instead of the overloaded == operator that C++ strings provide. You should use strncmp to compare your strings (it's safer than strcmp). Although it doesn't matter much here as you are comparing with a literal, which guarantees one of them will end.

c++ program won't output as expected

I'm writing a program to familiarize myself with input and command line arguments.
I'm trying to scan the user's input for either 'a' or 'b', and if it matches print the next argument given out. For some reason no matter what the user enters it always outputs as "Invalid." Can anyone see what I might be doing wrong?
int main(int argc, char* argv[])
{
if(argc != 5)
{ //checks if input is blank
cout << "Usage: <Function>, String, Usage: <Function>, String," << endl;
}
else
{
for(int i = 1; i<argc; i++)
{
cout << argv[i] << endl;
if(argv[i][1] == 'a')
{
cout << argv[i] << "ASCII" << endl;
}
if(argv[i][1] == 'b')
{
cout << argv[i] << "BINARY" << endl;
}
else
{
cout << "incorrect format" << endl;
}
}
}
}
argv[i][1] is the second character of the string argv[i] because arrays in C++ are zero-based.
I think you may want to use argv[i][0] instead, the first character.
See the following code for a sample:
#include <iostream>
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
std::cout << " Argument: " << argv[i] << '\n';
std::cout << " First: " << argv[i][0] << '\n';
}
return 0;
}
Running that as per the following transcript, gives the expected output:
pax> testprog alpha beta gamma delta epsilon
Argument: alpha
First: a
Argument: beta
First: b
Argument: gamma
First: g
Argument: delta
First: d
Argument: epsilon
First: e

How enable dragging a file on the *.exe and get it as parameter?

What do I have to do to make my program use a file that has been dragged and dropped onto its icon as a parameter?
My current main method looks like this:
int main(int argc, char* argv[])
{
if (argc != 2) {
cout << "ERROR: Wrong amount of arguments!" << endl;
cout << "\n" << "Programm closed...\n\n" << endl;
exit(1);
return 0;
}
Converter a(argv[1]);
// ...
cout << "\n" << "Programm finished...\n\n" << endl;
// cin.ignore();
return 0;
}
What I'd really like to be able to do is select 10 (or so) files, drop them onto the EXE, and process them from within my application.
EDIT:
The incomming parameter is used as filename, constructed in the cunstructor.
Converter::Converter(char* file) {
// string filename is a global variable
filename = file;
myfile.open(filename.c_str(), ios_base::in);
}
The method where the textfile gets read:
string Converter::readTextFile() {
char c;
string txt = "";
if (myfile.is_open()) {
while (!myfile.eof()) {
myfile.get(c);
txt += c;
}
} else {
error("ERROR: can't open file:", filename.c_str());
}
return txt;
}
EDIT2:
deleted
Update:
I got again to this point.
Actual Main method:
// File path as argument
int main(int argc, char* argv[]) {
if (argc < 2) {
cout
<< "ERROR: Wrong amount of arguments! Give at least one argument ...\n"
<< endl;
cout << "\n" << "Programm closed...\n\n" << endl;
cin.ignore();
exit(1);
return 0;
}
vector<string> files;
for (int g = 1; g < argc; g++) {
string s = argv[g];
string filename = "";
int pos = s.find_last_of("\\", s.size());
if (pos != -1) {
filename = s.substr(pos + 1);
cout << "argv[1] " << argv[1] << endl;
cout << "\n filename: " << filename << "\n pos: " << pos << endl;
files.push_back(filename);
}
files.push_back(s);
}
for (unsigned int k = 0; k < files.size(); k++)
{
cout << "files.at( " << k << " ): " << files.at(k).c_str() << endl;
Converter a(files.at(k).c_str());
a.getATCommandsFromCSV();
}
cout << "\n" << "Programm finished...\n\n" << endl;
cin.ignore();
return 0;
}
Actually the console window apears for maybe 0.5 sec and closes again.
It doen't stop on any of my cin.ignore(); Maybe it doesn't get there?
Can anyone help?
Your program does not need to do anything special apart from handling command-line arguments. When you drag-drop a file onto an application in Explorer it does nothing more than to pass the file name as argument to the program. Likewise for multiple files.
If all you expect is a list of file names, then just iterate over all arguments, do whatever you want with them and be done. This will work for zero to almost arbitrarily many arguments.
Maybe you could write a test program like this:
int main(int argc, char* argv[])
{
// argv[0] is not interesting, since it's just your program's path.
for (int i = 1; i < argc, ++i)
cout << "argv[" << i << "] is " << argv[i] << endl;
return 0;
}
And see what happens after you throw different files at it.
EDIT: Just look at Joey's answer.
Answer to the main question
TO SEE THE ANSWER TO YOUR LAST PROBLEM SEE BOTTOM OF THIS ANSWER
All drag&dropped files are get-able as argv[orderOfTheFile] (orderOfTheFile is from 1-n),
however how does windows create that order, now that is a real mystery...
Anyway let's say I would create 26 plain text files ( *.txt ), from a.txt to z.txt on my Desktop,
now if I would drag&dropped them on my ArgsPrinter_c++.exe located directly on C:\ drive,
an output would be similar to this:
argc = 27
argv[0] = C:\ArgsPrinter_c++.exe
argv[1] = C:\Users\MyUserName\Desktop\c.txt
argv[2] = C:\Users\MyUserName\Desktop\d.txt
argv[3] = C:\Users\MyUserName\Desktop\e.txt
argv[4] = C:\Users\MyUserName\Desktop\f.txt
argv[5] = C:\Users\MyUserName\Desktop\g.txt
argv[6] = C:\Users\MyUserName\Desktop\h.txt
argv[7] = C:\Users\MyUserName\Desktop\i.txt
argv[8] = C:\Users\MyUserName\Desktop\j.txt
argv[9] = C:\Users\MyUserName\Desktop\k.txt
argv[10] = C:\Users\MyUserName\Desktop\l.txt
argv[11] = C:\Users\MyUserName\Desktop\m.txt
argv[12] = C:\Users\MyUserName\Desktop\n.txt
argv[13] = C:\Users\MyUserName\Desktop\o.txt
argv[14] = C:\Users\MyUserName\Desktop\p.txt
argv[15] = C:\Users\MyUserName\Desktop\q.txt
argv[16] = C:\Users\MyUserName\Desktop\r.txt
argv[17] = C:\Users\MyUserName\Desktop\s.txt
argv[18] = C:\Users\MyUserName\Desktop\t.txt
argv[19] = C:\Users\MyUserName\Desktop\u.txt
argv[20] = C:\Users\MyUserName\Desktop\v.txt
argv[21] = C:\Users\MyUserName\Desktop\w.txt
argv[22] = C:\Users\MyUserName\Desktop\x.txt
argv[23] = C:\Users\MyUserName\Desktop\y.txt
argv[24] = C:\Users\MyUserName\Desktop\z.txt
argv[25] = C:\Users\MyUserName\Desktop\a.txt
argv[26] = C:\Users\MyUserName\Desktop\b.txt
My ArgsPrinter_c++.exe source code:
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
cout << "argc = " << argc << endl;
for(int i = 0; i < argc; i++)
cout << "argv[" << i << "] = " << argv[i] << endl;
std::cin.ignore();
return 0;
}
Your last problem
I have created a simple program that creates only a sceleton of your class so it can be used, and the program's main itself ran JUST FINE => if your program exits too soon, the problem will be in your class...
Tested source code:
#include <iostream>
#include <vector>
using namespace std;
class Converter{
public:
Converter(const char* f){ cout << f << endl; }
void getATCommandsFromCSV(){ cout << "called getATCommandsFromCSV" << endl; }
};
int main(int argc, char* argv[]) {
vector<string> files;
for (int g = 1; g < argc; g++) {
string s = argv[g];
string filename = "";
int pos = s.find_last_of("\\", s.size());
if (pos != -1) {
filename = s.substr(pos + 1);
cout << "argv[1] " << argv[1] << endl;
cout << "\n filename: " << filename << "\n pos: " << pos << endl;
files.push_back(filename);
}
files.push_back(s);
}
for (unsigned int k = 0; k < files.size(); k++)
{
cout << "files.at( " << k << " ): " << files.at(k).c_str() << endl;
Converter a(files.at(k).c_str());
a.getATCommandsFromCSV();
}
cout << "\n" << "Programm finished...\n\n" << endl;
cin.ignore();
return 0;
}