How to split the elements of a text file in C++ - c++

i have a text file called builders.txt that contains some data
Reliable Rover:70:1.
Sloppy Simon:20:4.
Technical Tom:90:3.
Within my main file i have a function declaration related to this specific text file
void Builder() {
std:string name;
int ability;
int variability;
}
this is my read file function
std::vector<std::string> lines;
std::string inputFile1 = "Builders.txt";
std::string inputFile2 = "Parts.txt";
std::string inputFile3 = "Customers.txt";
std::string outputFile = "output.txt";
std::string input;
void readFile(std::string const& inputFile1, std::string const& inputFile2, std::string const& inputFile3,
std::vector<std::string>& lines) //function to read Builders, Customers and Parts text file
{
std::ifstream file1(inputFile1);
std::ifstream file2(inputFile2);
std::ifstream file3(inputFile3);
std::string line;
while(std::getline(file1, line))
{
lines.push_back(line);
}
while(std::getline(file2, line))
{
lines.push_back(line);
}
while(std::getline(file3, line))
{
lines.push_back(line);
}
}
This is my attempt
std::vector<std::string> lines;
std::string inputFile1 = "Builders.txt";
std::istringstream newStream(inputFile1);
std::string input;
void readFile(std::string const& newStream,std::vector<std::string>& lines)
{
std::ifstream file1(newStream);
std::string line;
while(std::getline(file1, line,":"))
{
lines.push_back(line);
}
When i run this code i recieve the error "no instance of overload function getline"
My question is given the text file how can i split the text file so that, for example, Reliable Rover is the name, 70 is the ability and 1 is the variability for the 1st record. Another example would be Sloppy Simon being the name, 20 being the ability and 4 being variaiblity. If the question is to vague or requires futher details please let me know
Thankyou

As #thomas-sablik mentioned, a simple solution is to read the file line by line and read each element from the line:
std::ifstream f("builder.txt");
std::string line;
// read each line
while (std::getline(f, line)) {
std::string token;
std::istringstream ss(line);
// then read each element by delimiter
while (std::getline(ss, token, ':'))
std::cout << token << std::endl;
}
don't forget to include sstream for using stringstreams.
Note: refer to cppreference, third parameter of std::getline is delim and is a character but you pass it as a string. So change:
while(std::getline(file1, line,":"))
to:
while(std::getline(file1, line,':'))

Here is a naive approach I came up with:
std::string name;
int ability;
int variability;
char read;
while (ifs >> read) { // read until the end of the file
// adding read into name
while (read != ':') {
name += read;
ifs >> read;
}
ifs >> ability;
ifs >> read; // Remove ':'
ifs >> variability;
ifs >> read; // Remove '.'
// Code to deal with the three variables
name = "";
}
Hope it helps.

Related

Reading a csv line. Problem with the last element

I have the following function
void get_inspva(const std::string line){
std::cout<<") "<<line<<std::endl;
std::istringstream iss(line);
std::string lineStream;
while (getline(iss,lineStream,',')){
std::cout<<"["<<lineStream<<"]"<<std::endl;
}
}
When I have a line like this:
a_line="1618378741,521574939,1618378741.55,36.10726662693096,139.97386621160913,57.3723646402359,-1.9803314791186484,-0.5150965223798682,0.021916236872909,-0.02259244492482216,0.09830296322174402,3.404935980746039,GOOD";
and I call get_inspva(a_line) I get
[1618378741]
[521574939]
[1618378741.55]
[36.10726662693096]
[139.97386621160913]
[57.3723646402359]
[-1.9803314791186484]
[-0.5150965223798682]
[0.021916236872909]
[-0.02259244492482216]
[0.09830296322174402]
[3.404935980746039]
]GOOD
Why is the last cout not working as it should?
EDIT:
The lines I am getting them from a csv file like
std::ifstream file(input_file);
std::string input_line;
int i=0;
while(getline(file,input_line)){
// std::cout<<i<<") "<<input_line<<std::endl;
// Here we process the input_line
get_inspva(input_line);
}
file.close();

How to read string with spaces, until a '\r'?

I want to read a long string, and split it into smaller ones, where each new row of the big string is an entry for the smaller ones. However, I only want it to break at '\r', and not at spaces.
This is a sample code of what I'm doing right now:
std::vector<std::string> m_list;
std::ifstream input("data.txt");
std::string str;
std::getline(input, str);
std::istringstream iss(str);
std::string temp;
while (iss >> temp)
{
m_list.push_back(temp);
}
However, this code breaks the string upon encountering spaces, as well.
You where definitely on the right path, you actually just need to skip the direct string stream operation. This should work fine:
std::vector<std::string> m_list;
std::ifstream input("data.txt");
std::string str;
while (std::getline(input, str, '\r'))
{
m_list.push_back(str);
}

Reading a file into memory C++: Is there a getline() for std::strings

I was asked to update my code that reads in a text file and parses it for specific strings.
Basically instead of opening the text file every time, I want to read the text file into memory and have it for the duration of the object.
I was wondering if there was a similar function to getline() I could use for a std::string like i can for a std::ifstream.
I realize I could just use a while/for loop but I am curious if there is some other way. Here is what I am currently doing:
file.txt: (\n represents a newline )
file.txt
My Code:
ifstream file("/tmp/file.txt");
int argIndex = 0;
std::string arg,line,substring,whatIneed1,whatIneed2;
if(file)
{
while(std::getline(file,line))
{
if(line.find("3421",0) != string::npos)
{
std::getline(file,line);
std::getline(file,line);
std::stringstream ss1(line);
std::getline(file,line);
std::stringstream ss2(line);
while( ss1 >> arg)
{
if( argIndex==0)
{
whatIneed1 = arg;
}
argIndex++;
}
argIndex=0;
while( ss2 >> arg)
{
if( argIndex==0)
{
whatIneed2 = arg;
}
argIndex++;
}
argIndex=0;
}
}
}
Where at the end whatIneed1=="whatIneed1" and whatIneed2=="whatIneed2".
Is there a way to do this with storing file.txt in a std::string instead of a std::ifstream asnd using a function like getline()? I like getline() because it makes getting the next line of the file that much easier.
If you've already read the data into a string, you can use std::stringstream to turn it into a file-like object compatible with getline.
std::stringstream ss;
ss.str(file_contents_str);
std::string line;
while (std::getline(ss, line))
// ...
Rather than grab a line then try to extract one thing from it, why not extract the one thing, then discard the line?
std::string whatIneed1, whatIneed2, ignored;
if(ifstream file("/tmp/file.txt"))
{
for(std::string line; std::getline(file,line);)
{
if(line.find("3421",0) != string::npos)
{
std::getline(file, ignored);
file >> whatIneed1;
std::getline(file, ignored);
file >> whatIneed2;
std::getline(file, ignored);
}
}
}

Stream object to represent file input and then standard input?

As a noob C++ project, I am building a CLI game with a game loop that relies upon user input. For the purpose of testing, I would like to be able to pass a file name as a command line argument to the program, which will be treated "like standard input" until it is read through.
Consequently, I need a way to encapsulate an object that represents a file's contents and then std::cin once the file has been read through - basically prepending a file's contents to standard input. At least, this seems like it would be very nice, so that I can avoid having something like
std::istringstream retrieve_input(std::ifstream &file) {
std::string line;
if (std::getline(file, line))
return std::istringstream{line};
std::getline(std::cin, line);
return std::istringstream{line};
}
Is replacing this with some kind of custom class a good approach? Or can I mess with std::cin somehow to prepend my file contents to it?
To clarify : My game loop might have say, 20 iterations. If I pass a file with five lines, I want to use those five lines for the first five iterations of my game loop, and then drop back to standard input for the remaining fifteen. I understand how to do this with a bunch of conditions, but I think there must be a way to nicely have this sort of behavior in a class. My problem is - what should it inherit from? std::streambuf? Is this a good idea in principle?
My (probably bad attempt)
class InputGrabber {
public:
virtual std::istringstream retrieve_line() = 0;
virtual ~InputGrabber() {}
};
class BaseInputGrabber : public InputGrabber {
public:
BaseInputGrabber(std::istream &_in): in{_in} {}
std::istringstream retrieve_line() override {
std::string line;
std::getline(in, line);
return std::istringstream{line};
}
private:
std::istream &in;
};
class VarInputGrabber : public InputGrabber {
public:
VarInputGrabber(std::istream &_in, const std::string &f_name) :
in{_in}, init{std::ifstream(f_name)} {}
std::istringstream retrieve_line() override {
std::string line;
if (std::getline(init, line)) {
return std::istringstream{line};
}
std::getline(in, line);
return std::istringstream{line};
}
private:
std::istream &in;
std::ifstream init;
};
Would the below work? You open the file, redirect std::cin to read from the file. Use std::getline on std::cin so that it reads from the file stream. Some time before the program ends or if the file is finished being read, restore std::cin back to its original state.
#include <iostream>
#include <fstream>
int main(int argc, const char * argv[]) {
std::fstream file("/users/Brandon/Desktop/testingInput.txt", std::ios::in);
//Redirect cin to a file.
std::streambuf* cinBuffer = std::cin.rdbuf();
std::cin.rdbuf(file.rdbuf());
//Read line from std::cin (which points to the file) with std::getline.
std::string line;
std::getline(std::cin, line);
std::cout<<"INPUT LINE: "<<line<<std::endl;
//Redirect cin to standard input.
std::cin.rdbuf(cinBuffer);
//Read some line from the user's input..
std::getline(std::cin, line);
std::cout<<"USER INPUT LINE: "<<line<<std::endl;
std::cin.get();
return 0;
}
You could always redirect from file via argv. Treat the whole thing as std::cin from the beginning.
Is the following example that what you are looking for?
std::string readNextLine(std::istream& is)
{
std::string line;
if(!std::getline(is, line))
std::getline(std::cin, line);
return line;
}
int main()
{
std::stringstream ss ("a\nb\nc\n");
for(int i = 0; i < 5; ++i)
{
auto line = readNextLine(ss);
std::cout << "got: " << line << std::endl;
}
return 0;
}
The stringstream can be replaced with fstream or any other type that implements std::istream.
You can test it here http://cpp.sh/5iv65

Processing a file line by line and parsing the line to get separate variables

So I'm new to C++ and I am trying to take a file that looks like this:
0 A
1 B
2 C
3 D
4 E
I need to process this file line by line obviously and then in each line I need to take the two numbers from the line and put them in a map where the number is an ID # for a Node with a name that is a string so 0 would be the key for node A in my map.
I think I have the line by line part down but even that is a little rocky.
Here is what I have:
bool read_index(map<long, string> &index_map, string file_name)
{
//create a file stream for the file to be read
ifstream index_file(file_name);
//if file doesn't open then return false
if(! index_file.is_open())
return false;
string line;
//read file
while(! index_file.eof())
{
getline(index_file,line);
stringstream ss(line);
while(ss)
{
//process line?
}
}
//file read
return true;
}
If there are no spaces in the string, then you can ignore the newlines and just extract tokens:
void read_index(std::istream & infile, std::map<long, std::string> & index_map)
{
long n;
std::string token;
while (infile >> n >> token) { index_map[n] = std::move(token); }
}
The error checking for opening the file should happen separately, before you call this function. Otherwise your function is doing Too Much.
If you have arbitrary strings, which may include whitespace, you need to use getline:
for (std::string line; std::getline(infile, line); )
{
std::istringstream iss(line);
long n;
std::string token;
if (iss >> n >> std::ws && std::getline(iss, token))
{
index_map[n] = std::move(token);
}
else
{
// unparsable input line
}
}
As you can see, the benefit of line-based processing is that you can also handle invalid lines and skip over those. The first code that was purely token based stops once and for all as soon as it cannot recognize a token.
You are misusing eof(). Try something more like this instead:
bool read_index(map<long, string> &index_map, string file_name)
{
//create a file stream for the file to be read
ifstream index_file(file_name);
//if file doesn't open then return false
if(!index_file)
return false;
string line;
//read file
while(getline(index_file, line))
{
stringstream ss(line);
long n;
std::string token;
if (ss >> n >> token)
{
//process line
index_map[n] = token;
}
else
{
// error do something
}
}
//file read?
return !file.fail();
}