C++ How to pass command line argument to read txt file - c++

What I've been trying to do is...
1) to read txt files by command line argument,
2) to use strings in the txt files as arguments for the main method (or whatever method you need to invoke).
For example, there are two txt files, one of which is named character.txt and the other match.txt.
The contents of the files would be like this.
character.txt
//This comprises of six rows. Each of the rows has two string values
Goku Saiyan
Gohan Half_Saiyan
Kuririn Human
Piccolo Namekian
Frieza villain
Cell villain
match.txt
//This comprises of three rows, each of them is one string value
Goku Piccolo
Gohan Cell
Kuririn Frieza
If I use those strings without using command line, I'd declare the strings in character.txt like this.
typedef string name; //e.g. Goku
typedef string type; //e.g. Saiyan, Human, etc
Now I'm looking for how to read and send string values from txt files like the ones above, and to use them for functions inside the main method, ideally like this way.
int main(int argc, char *argv)
{
for (int i = 1; i < argc; i++) {
String name = *argv[i]; //e.g. Goku
String type = *argv[i]; //e.g. Saiyan, Human, etc
String match = * argv[i]; //Goku Piccolo
//I don't think any of the statements above would be correct.
//I'm just searching for how to use string values of txt files in such a way
cout << i << " " << endl; //I'd like to show names, types or matchs inside the double quotation mark.
}
}
Ideally, I'd like to invoke this method in this way.
According to this web site., at least I understand it is possible to use command line arguments with C++, but I cannot find any more information. I'd appreciate if you'd give any advice on it.
PS. I'm using Windows and Code Blocks.

Asuming you just want to read contents of the files and process it, you can start with this code (Without any errors checks tho). It simply gets filenames from command line and reads file contents into 2 vectors. Then you can just process these vectors as u need.
#include <string>
#include <fstream>
#include <iostream>
#include <vector>
std::vector<std::string> readFileToVector(const std::string& filename)
{
std::ifstream source;
source.open(filename);
std::vector<std::string> lines;
std::string line;
while (std::getline(source, line))
{
lines.push_back(line);
}
return lines;
}
void displayVector(const std::vector<std::string&> v)
{
for (int i(0); i != v.size(); ++i)
std::cout << "\n" << v[i];
}
int main(int argc, char **argv)
{
std::string charactersFilename(argv[1]);
std::string matchesFilename(argv[2]);
std::vector<std::string> characters = readFileToVector(charactersFilename);
std::vector<std::string> matches = readFileToVector(matchesFilename);
displayVector(characters);
displayVector(matches);
}

to see how to use command line arguments look at this.
http://www.cplusplus.com/articles/DEN36Up4/
you cannot use the contents of the file which you have passed to your app through command line arguments. only the name of the file is passed to the app.
you should open the file using that name and read its contents. take a look at this:
http://www.cplusplus.com/doc/tutorial/files/

First the main function prototype should be
int main(int argc, char **argv)
OR
int main(int argc, char *argv[])
Second after retrieving files names in the main function you should open each file and retrieve its contents
Third Sample code
int main(int argc, char* argv[])
{
for(int i=1; i <= argc; i++) // i=1, assuming files arguments are right after the executable
{
string fn = argv[i]; //filename
cout << fn;
fstream f;
f.open(fn);
//your logic here
f.close();
}
return 0;
}

You define main prototype incorrectly. You also need std::ifstream to read files.
If you expect exactly two arguments, you may check argc and extract arguments directly:
int main(int argc, char* argv[]) {
if(argc != 3) {
std::cerr << "Usage: " << argv[0]
<< " name.txt match.txt" << std::endl;
return 1;
}
std::ifstream name_file(argv[1]);
std::ifstream match_file(argv[2]);
// ...
return 0;
}
If you expect unspecified number of files, than you need a loop and an array to save them, i.e. vector:
int main(int argc, char* argv[]) {
std::vector<std::ifstream> files;
for(int i = 1; i < argc; ++i)
files.emplace_back(argv[i]);
// ...
return 0;
}
And do not forget to check if files are openable.

#include<stdio.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp = fopen( argv[1], "r");
char line[50];
if (fp == NULL)
{
printf("File opening Unsuccessful\n");
exit(1);
}
while (fgets(line , 30 , fp) != NULL)
{
printf("%s",line);
}
fclose(fp) ;
return 0;
}

Related

Batch one line to call an executable using arguments from file

For convenience, I have renamed all the files to simple names for my example.
I'm trying to run an executable (test.exe), with a C++ entrypoint int main(int argc, char* argv[]) from a batch file (test.bat), and pass arguments from a text file (test.txt). The end goal is to run unit tests on an SDK using the testing software (test.exe).
My issue is that I do not want to have to use a variable when I call the executable since it makes the code harder to read :
rem This works
set /p test_input=<test.txt& call test.exe %test_input%
After some research, I figured I should use input redirection like so :
rem This does not work
call test.exe < test.txt
This does not work, and I don't understand why.
This is what I initially tried, and it has been suggested before on SO (here).
I have access to the test.exe code, so I can print argc and argv :
int main(int argc, char* argv[])
{
if(new_argc >= 2)
{
if(strcmp("-help", argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < argc; i++)
{
if(strcmp("-framerate", argv[i]) ==0)
{
i++;
if(i < argc)
{
FrameRate = (float)atof(argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
}
If I enter the arguments and parameters manually, it works as expected.
test.txt
-arg1 param1 -arg2 param2 ...
test.bat
call test.exe < test.txt
Output : test.exe runs as if there are no arguments or parameters.
Edit :
Added a few details about the entrypoint and renamed the batch variable.
Thanks to the comments under my question, I was pushed in the right direction.
The problem was my understanding of <. It literally means "Read file to STDIN" (as mentionned here). Many other documentation sites give vague definitions like (as mentionned here)
command < filename : Type a text file and pass the text to command
I need to parse the input correctly, since stdin isn't available in argc or argv, but through std::cin.
My batch code and text file remain unchanged, and I want to maintain the same form of parsing to avoid rewriting multiple projects, so I split the input string using the Solution 1.3 from here (slightly modified) and created a new_argv.
std::vector<char*> split(const std::string& s, char delimiter)
{
std::vector<char*> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter))
{
tokens.push_back(_strdup(token.c_str()));
}
return tokens;
}
int main(int argc, char* argv[])
{
std::string extra_input; // Variable to store the contents of test.txt
std::getline(std::cin, extra_input); // Recuperate the contents of test.txt
std::vector<char*> new_argv = split(extra_input, ' '); // Split the args
for(int i = argc - 1; i >= 0; i--)
new_argv.insert(new_argv.begin(), argv[i]); // Add the original args to the beginning
const size_t new_argc = new_argv.size(); // Create the new argc based on the final argument list (vector)
if(new_argc >= 2)
{
if(strcmp("-help", new_argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < new_argc; i++)
{
if(strcmp("-framerate", new_argv[i]) ==0)
{
i++;
if(i < new_argc)
{
FrameRate = (float)atof(new_argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
// Important, don't forget to free the memory used by the _strdup
for(int i=1; i < new_argc; i++)
{
if(i >= argc)
free(new_argv[i]);
}
}
test.bat
call test.exe < test.txt
test.txt
-arg1 param1 -arg2 param2 ...
Of course, I need to add some checks to make it properly handle whitespace, but that's the gist of it. Thank you for your help and external point of view.
Edit : Fixed a mistake in the code.

map.insert of * argv[]

I have some problem to understand which parameters would be the right once at the insert function of my <map>, maybe you can help me and explaine why?
I wanna open a file, and also save the name of the file which is given by the user over the arguments. To do this I thought it is a good soultion to make a map with a ifsteam, and a string object.
Here is the code:
int main(int argc, char** argv)
{
std::map<std::ifstream*, std::string> Dateien;
auto it_dateien = Dateien.begin();
for(size_t param = 1; param < argc; param++)
{
//No valid instance of the constructor
Dateien.insert(it_dateien,std::pair<std::ifstream*, std::string((*argv[],*argv[]));
Thanks in advance!
If I understand correctly what you want to achieve you need something like this:
int main(int argc, char** argv)
{
std::map<std::string, std::ifstream> Dateien;
for (size_t param = 1; param < argc; param++)
{
Dateien[argv[param]] = std::ifstream{ argv[param] };
}
}
I assume that you want to save in a map the name of the file you want to read, and to retreive the corresponding ifstream by doing Datein.at(NameOfFile).
For example the usage can be something like this:
std::string line;
auto& firstFile = Dateien.at(argv[1]);
if (firstFile.is_open())
{
while (std::getline(firstFile, line))
{
std::cout << line << '\n';
}
firstFile.close();
}

how to read input files taken as parameters in C++

I've just practiced coding C++. I knew in Java, we could have a public main method, which could read input file names as parameters of the string array argument. But I wonder how I do the same task in C++?
Both Java and C++ follow the same C-like syntax. So it doesn't really differ from what you had in Java. In Java you had a string class :
class Test {
public static void main(String args[]) {
for(int i = 0; i < args.length; i++)
System.out.println("Argument " + i + " = " + args[i]);
}
}
C/C++ mostly use primitive character arrays in order to store strings. Although Standard Template Library also provides string classes, but C++ uses native char arrays to store commandline arguments. The main function takes two variables :
int argc : number of commandline arguments
char *argv[] : an array of character strings
You can also say it can be written as char **argv, because of the underlying representation of two dimensional arrays in C/C++, but both mean the same thing. The equivalent of the above code in C++ would be:
#include <iostream>
int main(int argc, char *argv[]) {
for(int i = 0; i < argc; i++)
std::cout << "Argument " << i << " = "
<< argv[i] << std::endl;
return 0;
}
You do it the same way, with slightly different syntax because C arrays do not store their length, so it is passed as a separate parameter.
int main(int argc, char** argv) {
// Read args from argv, up to argc args.
// argv[0] is the name of the program
// argv[1] is the first argument
}
The main function gives you the argument count and the actual arguments as an array of character arrays.
To safely work with this, you should first turn this information into a std::vector<std::string>.
#include <string>
#include <vector>
int main(int argc, char *argv[]) {
std::vector<std::string> arguments;
for (int index = 0; index < argc; ++index) {
arguments.push_back(argv[index]);
}
}
You will notice that arguments[0] is equal to the filename of the executable (in theory, this depends on the system you're using). If you are on Windows and have an executable called stackoverflow.exe, then starting it with
stackoverflow.exe one two
would result in arguments containing { "stackoverflow.exe", "one", "two" }.

Creating a vector of structs using command line arguments

I am having some trouble understanding the process of creating this vector of structs. I am passing files into the program as parameters, and using their file locations to create a struct that contains this information. Is there a way to create the struct and add it to the vector using struct functions?
struct fileDetails
{
string filePath;
string fileName;
string fileExt;
void setFileDetails(char** path);
};
void fileDetails::setFileDetails(char** path)
{
filePath = path.substr(0, path.find_last_of('\\'));
fileExt = path.substr(path.find_last_of(".") + 1);
fileName = path.substr(path.find_last_of('\\') + 1);
fileName = fileName.substr(0, fileName.find_last_of('.'));
}
int main(int argc, char** argv[])
{
vector<fileDetails> fileList;
fileDetails fDet;
for (int i = 0; i < argc; i++)
{
fDet.setFileDetails(argv[i]);
fileList.push_back(fDet);
}
}
Or what would be the better approach to this? I tried to do this using this format, but I am getting errors.
You have two main errors.
First of all, char** does not have any substr member function. What you probably meant was to use std::string instead:
void setFileDetails(std::string path);
// ^^^^^^^^^^^
And second, you have an unnecessary * in argv:
int main(int argc, char** argv[])
// ^
If you fix both, the program will compile just fine.

Getting line from a txt file using fstream

int main(int argc, const char * argv[])
{
ifstream input;
input.open("test.txt");
string arrAtoms[700];
string temp;
int i = 0;
while(getline(input, temp)){
if(startsWithAtom(temp)) {
arrAtoms[i] = temp;
i++;
}
}
return 0;
}
bool startsWithAtom(string test) {
string atom = "ATOM";
if(test.find(atom) == 0) {
return true;
}
return false;
}
So this is my code to read a line and store it in arrAtoms[] if it starts with "ATOM".
For some reason, I keep getting the error Thread1: EXC_BAD_ACCESS(code=EXC_1386_GPFLT)
and I have no clue why. Please help!
The code runs quite fine on my machine. Maybe the problem is that the file has more ATOM entries than 700? And your string array can only containg 700. If you don't know how many entries there will be, try using a vector
This is the file I tested the code on:
soadiaodiaodsa
sdaiod sadoiasoda
ATOM alodaskd
ATOM alosad
ATOM lol
saodai aosdisoad daiosiadsa
ATOM ATOM ATOM
ATOM LOL test
lololololol
I also tried outputting the first 15 entries in the array and it works fine and consists only of lines starting with ATOM:
for(unsigned int i=0;i<15;i++)
cout << arrAtoms[i] << endl;
You are using array with length 700. If your file have more than 700 lines that start with "ATOM", a memory allocation error will happen. A better way to do this is to use vector, so you don't need to worry about the size of the file.
#include <vector>
int main(int argc, const char * argv[])
{
ifstream input;
input.open("test.txt");
std::vector <string> arrAtoms;
string temp;
while(getline(input, temp)){
if(startsWithAtom(temp)) {
arrAtoms.push_back(temp);
}
}
return 0;
}