Command Line Arguments With "-" Prefix - c++

I'm trying to write a program which I'll be able to start with custom arguments. Like in this example "program.exe -width 1920 -height 1080". I wrote a simple code, which should work.
#include <iostream>
int main(int argc, char* argv[])
{
for (int i = 1; i < argc; i++)
{
if (argv[i] == "-width")
{
std::cout << "Width: " << argv[++i] << "\n";
}
else if (argv[i] == "-height")
{
std::cout << "Height: " << argv[++i] << "\n";
}
}
return 0;
}
And this program doesn't work. It's not displaying anything. I also tried checking this code line by line with debugger, but when argv[i] == "-width" it just skips it.
Is there a way to fix it or there are just some other methods of doing this?

You are comparing pointers, not strings. To compare strings via ==, you should use std::string.
Also you should check if the elements argv[++i] exists.
#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
for (int i = 1; i < argc; i++)
{
if (argv[i] == std::string("-width") && i + 1 < argc)
{
std::cout << "Width: " << argv[++i] << "\n";
}
else if (argv[i] == std::string("-height") && i + 1 < argc)
{
std::cout << "Height: " << argv[++i] << "\n";
}
}
return 0;
}

You can also use the s suffix
cout << ("wilson" == "wilson"s) << endl;
output:
1

Related

How do I make a command line argument into a vector with each character as a different value?

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
/*
void square(cLine, height){
}
*/
//void insertVector(vector<char> & cLine, )
int main(int argc, char *argv[]){
if (argc != 5){
cerr << "Incorrect number of arguments" << endl;
return 1;
}
ofstream writeFile(argv[4]);
if (!writeFile.good()){
cerr << "Bad file, could not open" << endl;
return 1;
}
int arrayLength = sizeof(argv[1]);
vector<string> cLine;
string cString(argv[1]);
for(int i=0,i<arrayLength-2,++i){
cLine.push_back(cString.substr(i,i+1));
}
cout << cLine << endl;
}
I'm trying to take an argument argv[1] and make it into a vector so I can easily splice it later on, I understand the argument is a c style char* array but I don't how to convert it into a vector where each index is exactly a character, yet is printable to console.
This declaration
int arrayLength = sizeof(argv[1]);
does not make sense because sizeof(argv[1]) is equivalent to sizeof( char * ) and does not yield the number of characters in the argument.
This statement
cout << cLine << endl;
also does not make sense because there is no overloaded operator << for the template class std::vector.
Moreover this for loop
for(int i=0,i<arrayLength-2,++i){
is syntactically incorrect.
What you need is the following
#include <cstring>
//…
std::vector<char> cLine( argv[1], argv[1] + std::strlen( argv[1] ) );
for ( const auto &c : cLine ) std::cout << c;
std::cout << '\n';
You could create std::strings (which are somewhat similar to std::vectors) out of the arguments. It'll make it easy to do what you'd like.
I'll refer to constructor numbers # std::basic_string
and std::vector in the code.
#include <iostream>
#include <string>
#include <vector>
int cppmain(std::string program, std::vector<std::string> args) {
if(args.size() != 4) {
std::cerr << "Incorrect number of arguments\n";
return 1;
}
std::cout << program << " got arguments:\n";
for(std::string& arg : args) {
std::cout << " " << arg << "\n";
}
std::cout << program << " got arguments (alt. version):\n";
for(size_t i = 0; i < args.size(); ++i) {
std::cout << " " << args[i] << "\n";
}
std::cout << "the first argument, char-by-char:\n";
for(char ch : args[0]) {
std::cout << ch << '\n';
}
std::cout << "the first argument, char-by-char (alt. version):\n";
for(size_t i = 0; i < args[0].size(); ++i) {
std::cout << args[0][i] << '\n';
}
if(args[0].size() >= 6) {
std::cout << "printing a range (start=2, length=4) of the first argument:\n";
std::cout << args[0].substr(2, 4) << '\n';
}
return 0;
}
// Let main() create a std::string out of the program name (argv[0]) using
// std::string constructor 5
//
// and a std::vector<std::string> out of the arguments using
// std::vector constructor 5 (iterators).
//
// {argv+1, argv+argc} becomes arguments to the args parameter in cppmain()
//
// argv+1 points at the first program argument argv[1] (the begin iterator)
// argv+argc points one step beyond the last program argument (the end iterator)
int main(int argc, char* argv[]) {
return cppmain(argv[0], {argv + 1, argv + argc});
}

Why can't the program find manually appended '#' (hashtags) in a string?

When I run this program:
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char** argv)
{
if (argc != 2)
{
cerr << "[ERR] usage: " << argv[0] << " expression" << endl;
return 1;
}
string pExpression = argv[1];
size_t stringLength = pExpression.length();
if (pExpression[stringLength - 1] != '#') //If there is no hashtag at the end, append one
pExpression += '#';
cout << "Search for '#' in '" << pExpression << "'..." << endl;
bool found = false;
for (size_t i = 0;i < stringLength;i++)
{
if (pExpression[i] == '#')
found = true;
}
cout << ((found) ? "String contains '#'" : "String doesn't contain '#'") << endl;
return 0;
}
I try to check, if there is a hashtag at the end. When there is none, I append one.
However, if I check for it, my programm "can't find" it.
Here is an example on what I mean:
Because you don't increase stringLength after you add the #. So the for loop stops right in front of it.

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

Three questions regarding command line arguments / char*

My post is organized in three sections:
1. My code
2. Example input and output
3. My three questions
MY CODE:
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>
#include <cstring>
using namespace std;
void deleteTrash(char*, char*);
const int kStr = 2;
const int kStrLen = 3;
int main(int argc, char* argv[])
{
if (argc < 4) {
cout << "Incorrect argument given." << endl;
cout << "Try again." << endl;
return 0;
}
cout << "PRINT argv[2]" << endl;
cout << "-----" << endl;
for (int i = 0; i < sizeof(argv[2]); i++) {
cout << "Iterator: " << i << endl;
cout << argv[2][i] << endl;
}
char* inputString;
deleteTrash(argv[kStr], inputString);
cout << "PRINT inputString" << endl;
cout << "-----" << endl;
for (int i = 0; i < sizeof(inputString); i++) {
cout << i << endl;
cout << inputString[i] << endl;
}
int strLen;
stringstream num;
num << argv[kStrLen];
num >> strLen;
if ( num.fail() ) {
cout << "Incorrect argument given." << endl;
cout << "Try again." << endl;
return 0;
}
if ( strLen < sizeof(inputString) ) {
cout << "Incorrect argument given." << endl;
cout << "Try again." << endl;
return 0;
}
return 0;
}
void deleteTrash(char* tempString, char* inputString)
{
int tempStringLen = sizeof(tempString);
int newSize = 0;
while (tempString[newSize] != '\0')
newSize++;
char newString[newSize + 1];
int iterator = 0;
while (tempString[iterator] != '\0') {
newString[iterator] = tempString[iterator];
iterator++;
}
newString[newSize] = '\0';
cout << "PRINT newString" << endl;
cout << "-----" << endl;
for (int i = 0; i < sizeof(newString); i++) {
cout << newString[i] << endl;
}
inputString = newString;
cout << "PRINT inputString" << endl;
cout << "-----" << endl;
for (int i = 0; i < sizeof(inputString); i++) {
cout << "Iterator: " << i << endl;
cout << inputString[i] << endl;
}
return;
}
EXAMPLE INPUT:
./hw1q5 4 W# 3
OUTPUT:
PRINT argv[2]
-----
Iterator: 0
W
Iterator: 1
#
Iterator: 2
Iterator: 3
3
Iterator: 4
Iterator: 5
T
Iterator: 6
E
Iterator: 7
R
PRINT newString
-----
W
#
PRINT inputString
-----
Iterator: 0
W
Iterator: 1
#
Iterator: 2
Iterator: 3
Iterator: 4
Iterator: 5
Iterator: 6
Iterator: 7
PRINT inputString
-----
0
Segmentation fault: 11
MY QUESTIONS:
Why does argv contain more than 3 elements (M, #, and \0). It prints out 8 elements (print statement iterates 0 - 7), which, after W, #, \0, are garbage. Should it not be printing only the 3 elements (M, #, and \0). Why is this happening? How may I fix it?
Why is it that when I set newString (type char) to inputString (type char*), by doing inputString = newString, inputString iterates 8 time in the print statement, printing blanks after printing M, #, and \0.
Why is the seg fault happening in the third statement?
sizeof() does not return the length of a null-terminated character array string. Instead you need something like strlen().
Let's just take one of the problems here:
// Wrong!
int main(int argc, char* argv[])
...
for (int i = 0; i < sizeof(argv[2]); i++) {
cout << "Iterator: " << i << endl;
cout << argv[2][i] << endl;
}
// Better
int main(int argc, char* argv[])
...
for (int i = 0; i < argc; i++) {
cout << "argv[" << i << "]: " << argv[i] << endl;
}
argv[] is an array of one or more "C" strings.
argc tells you how many strings are in the array.
You want to iterate through the strings in the array (argv[i]), not the characters in the string (for example, "argv[0][0]").
... AND ...
"sizeof(argv)" just gives you the size of a pointer (4 bytes, for a 32-bit CPU). It does NOT give you the #/elements in the array. That's what "argc" is for.
In answer to your first question, I'm going to refer you to another SO article: What does int argc, char *argv[] mean?
The first command line argument is always the command itself. So in your example:
./hw1q5 4 W# 3
There are four command line arguments: hw1q5, 4, W#, and 3.
In regards to your other questions, and the remainder of the first question, the majority of your problems stem from the assumption that sizeof(char*) returns the length of a null terminated string, which it does not (as has been pointed out both in comments and an earlier answer).
A good reference for understanding sizeof can be found here: http://en.cppreference.com/w/cpp/language/sizeof, or as I suspect this is a homework assignment based on your compiled program name, your C++ textbook.

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;
}