C++ - How to declare a variable number of files as input? - c++

I've got a problem:
When the number of input files is known, for example: 2 files, the declaration will be very simple:
int main(int argc, const char** argv) {
const char* inputfile1 = argv[1];
const char* inputfile2 = argv[2];
const char* outputfile = argv[3];
cout << "Appending "
<< inputfile1 << " and "
<< inputfile2 << " to "
<< outputfile << "..." << endl;
...
...
}
But now the NUMBER of input files is unknown, how to make the declaretion? Thx

Use loops, Luke!
int main(int argc, const char** argv) {
cout << "Appending ";
for(int i = 1; i < argc-1; i++)
cout << argv[i] << (i != argc-2 ? " and " : " to ");
cout << argv[argc-1] << "..." << endl;
...
...
}

Use an array, a vector, a list, or some other STL container, e.g. std::vector<std::string> for the names and std::vector<std::ostream> for the output streams.

Related

Command Line Argument adding an extra parameter

I am trying to explore command line argument in C++ in Visual Studio 2010. It is working fine but it is adding one extra parameter at args[0] any reason why?
Here is the snapshot of how I am setting my arguments
the main method is as follows
int main(int argc, char *argv[])
{
cout<< endl << "total args passed in the command line - " << argc << endl;
for(int i = 0; i < argc; i++){
cout << i+1<< " argument - " << argv[i] << endl;
}
return 0;
}
The output on cmd is looking like this
As you can see that I am adding only two arguments but it is showing the count as 3.
The first entry in the list is the program name; that's how it's designed to work.
args[0] contains the name of your program and argc includes this in the count aswell.
If you would like to exclude this, try this:
int main(int argc, char *argv[])
{
cout<< endl << "total args passed in the command line - " << argc - 1 << endl;
for(int i = 1; i < argc; i++){
cout << i << " argument - " << argv[i] << endl;
}
return 0;
}

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

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