Three questions regarding command line arguments / char* - c++

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.

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

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]++;

how can I find the sequence number (index) of word in such a paragraph c++?

I'm working on a project which needs to find the number of words and the indices of each word in the paragraph ...I have written the code which is counting the number of word in a string but I stuck with finding the indices of words,
such as : Hi John How are you I miss you ..
I need to print the indices like : 0 1 2 3 4 5 6 7
here is the code:
int _tmain(int argc, _TCHAR* argv[])
{
int count_words(std::string);
std::string input_text;
std::cout<< "Enter a text: ";
std::getline(std::cin,input_text);
int number_of_words=1;
int counter []={0};
for(int i = 0; i < input_text.length();i++)
if(input_text[i] == ' ')
number_of_words++;
std::cout << "Number of words: " << number_of_words << std::endl;
//std:: cout << number_of_words << std::endl;
system ("PAUSE");
}
Hopefully this helps. Edited to include use of count_words function.
#include <iostream>
#include <sstream>
void count_words(std::string);
int main(){
std::string input_text, output_text;
std::cout<< "Enter a text: ";
std::getline(std::cin,input_text);
count_words(input_text);
system ("PAUSE");
return 0; //MUST RETURN AN INTEGER VALUE FROM 'INT MAIN'
}
void count_words(std::string inputString){
std::string output_text;
std::stringstream indexes;
int number_of_words=0; //If there are no words, it would be false, make it 0.
//int counter []={0}; //This serves no purpose.
if(!inputString.empty()){// test to make sure it isn't empty.
number_of_words++;
for(int i = 0; i < inputString.length();i++){ // For loops should have curly braces {} containing their statement.
if(inputString[i] == ' '){
number_of_words++;
}
if((isalpha(inputString[i]))&&inputString[i-1]==' '){ //test for following space separated word
indexes << i << " ";
}
}
}
output_text = indexes.str(); //convert stringstream to string
std::cout << "Number of words: " << number_of_words << std::endl;
//std:: cout << number_of_words << std::endl; //duplicate info
std::cout << "Indexes: " << output_text << std::endl;
}
I'm not sure if i understand the question. You only need print the "indices"?? like this? (Using your own code)
#include <iostream>
#include <vector>
#include <string>
void stringTokenizer(const std::string& str, const std::string& delimiter, std::vector<std::string>& tokens) {
size_t prev = 0, next = 0, len;
while ((next = str.find(delimiter, prev)) != std::string::npos) {
len = next - prev;
if (len > 0) {
tokens.push_back(str.substr(prev, len));
}
prev = next + delimiter.size();
}
if (prev < str.size()) {
tokens.push_back(str.substr(prev));
}
}
int main()
{
std::vector <std::string> split;
std::string input_text;
std::cout<< "Enter a text: ";
std::getline(std::cin,input_text);
stringTokenizer(input_text, " ", split);
int number_of_words = 0;
for (std::vector<std::string>::iterator it = split.begin(); it != split.end(); it++, number_of_words++) {
std::cout << *it << " " << number_of_words << std::endl;
}
}

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