Creating a vector of structs using command line arguments - c++

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.

Related

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 Loop Through a const char**?

I have a const char** called glfwNames which holds the C version of a string array of the required GLFW library extensions. Would it be possible to loop through either the const char* (string), or the individual characters of the string separated by '\0'?
const char** glfwNames = glfwGetRequiredInstanceExtensions(&glfwCount)
for (const char** name = glfwNames; *name; ++name)
{
slog("GLFW Extensions to use: %s", *name);
}
This is what I've attempted from one of the answers, and the return value of
glfwGetRequiredInstanceExtensions
is an array of extension names, required by GLFW http://www.glfw.org/docs/latest/group__vulkan.html#ga1abcbe61033958f22f63ef82008874b1
If glfwNames is nullptr-terminated:
#include <cstdio>
int main()
{
char const *glfwNames[] = { "foo", "bar", "baz", nullptr };
for (char const **p = glfwNames; *p; ++p)
std::puts(*p);
}
If you *know* the number of strings:
std::uint32_t glfwCount;
const char** glfwNames = glfwGetRequiredInstanceExtensions(&glfwCount)
for (std::uint32_t i{}; i < glfwCount; ++i)
{
slog("GLFW Extensions to use: %s", glfwNames[i]);
}
To also loop through the individual chars:
for (std::uint32_t i{}; i < glfwCount; ++i)
{
for(char const *p{ glfwNames[i] }; *p; ++p)
std::putchar(*p);
}
A common pattern that I use to loop through the arguments to main is via std::for_each:
#include <algorithm>
int main(int argc, char* argv[]) {
std::for_each( argv + 1, argv + argc, handler );
}
where handler is any function taking a const char*, const std::string&, or std::string_view (I use the later).
Would a similar approach work for your problem? Notice that this approach requires you to know the length of your array of strings.
As a side note, it is important to know that the return argument of std::for_each is the function provided (handler in this case). That enables the suggested pattern to make a last call once the input is known to have been exhausted:
#include <algorithm>
int main(int argc, char* argv[]) {
std::for_each( argv + 1, argv + argc, handler )("Argument To Last Call");
}
This can be used to implement state machines that receive the termination trigger at the end.

C++ How to convert char pointer array to string array?

In a C++ application, the arguments are all stored in a char* array, like so:
int main(int argc, char* argv[])
{
...
}
However, a lot of people prefer the convenience of string manipulation, but it would be a hastle to have to convert every char* into a std::string every time. So my question is, how do convert a char*[] into a std::string[], so that you don't have to convert them all individually as you progress in your program?
If you accept std::vector, you can use its range constructor.
std::vector<std::string> args(argv, argv + argc);
You can loop through the arguments.
int main(int argc, char* argv[]) {
std::string *s = new std::string[argc];
for (int i = 0; i < argc; ++i)
s[i] = argv[i];
}

Turn std::string into array of char* const*'s

I am writing a command shell in C++ using the POSIX api, and have hit a snag. I am executing via execvp(3), so I somehow need to turn the std::string that contains the command into a suitable array of char* consts*'s that can be passed to:
int execvp(const char *file, char *const argv[]);
I have been racking my brain for hours but I can't think of any realistic or sane way to do this. Any help or insight on how I can achieve this conversion would be greatly appreciated. Thank you and have a good day!
edit:
As per request of Chnossos, here is an example:
const char *args[] = {"echo", "Hello,", "world!"};
execvp(args[0], args);
Assuming you have a string that contains more than "one argument", you will first have to split the string (using a std::vector<std::string> would work to store the separate strings), then for each element in the vector, store the .c_str() of that string into a const char args[MAXARGS] [or a std::vector<const char*> args; and use args.data() if you don't mind using C++11]. Do not forget to store a 0 or nullptr in the last element.
It is critical if you use c_str that the string you are basing that of is not a temporary: const char* x = str.substr(11, 33).c_str(); will not give you the thing you want, because at the end of that line, the temporary string is destroyed, and its storage freed.
If you have only one actual argument,
const char* args[2] = { str.c_str(), 0 };
would work.
Examplary approach:
#include <string>
#include <vector>
#include <cstring>
using namespace std;
int execvp(const char *file, char *const argv[]) {
//doing sth
}
int main() {
string s = "echo Hello world!";
char* cs = strdup(s.c_str());
char* lastbeg = cs;
vector<char *> collection;
for (char *itcs = cs; *itcs; itcs++) {
if (*itcs == ' ') {
*itcs = 0;
collection.push_back(lastbeg);
lastbeg = itcs + 1;
}
}
collection.push_back(lastbeg);
for (auto x: collection) {
printf("%s\n", x);
}
execvp("abc.txt", &collection[0]);
}
Notice that the memory for the cs isn't freed here... in your application you would need to take care of that...
The number of elements in array can be simply extracted from collection.size()
I use this:
command_line.hpp:
#pragma once
#include <vector>
#include <string>
namespace wpsc { namespace unittest { namespace mock {
class command_line final
{
public:
explicit command_line(std::vector<std::string> args = {});
explicit command_line(int argc, char const * const * const argv);
int argc() const;
/// #remark altering memory returned by this function results in UB
char** argv() const;
std::string string() const;
private:
std::vector<std::string> args_;
mutable std::vector<char*> c_args_;
};
}}} // wpsc::unittest::mock
command_line.cpp:
#include <wpsc/unittest/mock/command_line.hpp>
#include <algorithm>
#include <sstream>
namespace wpsc { namespace unittest { namespace mock {
command_line::command_line(std::vector<std::string> args)
: args_( std::move(args) ), c_args_( )
{
}
command_line::command_line(int argc, char const * const * const argv)
: command_line{ std::vector<std::string>{ argv, argv + argc } }
{
}
int command_line::argc() const
{
return static_cast<int>(args_.size());
}
char ** command_line::argv() const
{
if(args_.empty())
return nullptr;
if(c_args_.size() != args_.size() + 1)
{
c_args_.clear();
using namespace std;
transform(begin(args_), end(args_), back_inserter(c_args_),
[](const std::string& s) { return const_cast<char*>(s.c_str()); }
);
c_args_.push_back(nullptr);
}
return c_args_.data();
}
std::string command_line::string() const
{
using namespace std;
ostringstream buffer;
copy(begin(args_), end(args_), ostream_iterator<std::string>{ buffer, " " });
return buffer.str();
}
}}} // wpsc::unittest::mock
Client code:
int main(int argc, char** argv)
{
wpsc::unittest::mock::command_line cmd1{ argc, argv };
// wpsc::unittest::mock::command_line cmd2{ {"app.exe" "-h"} };
some_app_controller c;
return c.run(cmd1.argc(), cmd1.argv());
}
If the parsing can actually be really complicated, I'd go with something like that:
std::string cmd = "some really complicated command here";
char * const args[] =
{
"sh",
"-c",
cmd.c_str(),
(char *) NULL
};
execvp(args[0], args);
So the problem is the splitting of the line into individual arguments, and filling the argument vector with the respective pointers?
Assuming you want to split at the whitespace in the line, you replace whitespace in the string with null-bytes (in-place). You can then fill the argument vector with pointers into the string.
You will have to write a single loop to go through the string.
You need to decide what the rules will be for your shell and implement them. That's a significant fraction of the work of making a shell.
You need to write this code, and it's not simple. In a typical shell, echo "Hello world!" has to become { echo, Hello world! }, while echo \"Hello world!\" has to become { echo, "Hello world!" }. And so on.
What will " do in your shell? What will ' do? You need to make these decision before you code this part.

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

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