Reading in from .txt - c++

I have a .txt file with names and grades such as "emiltytaco 56". After the last name, there are 3 blank lines which should not be inserted into my trie and heap. However the code is Aborted and dumped when hitting the blank lines. This is the insert function
while(myfile.good())
{
getline(myfile, line);
name = line.substr(0,line.find("\t"));
stringstream convert((line.substr(line.find("\t")+1)));
convert >> grade;
if(name.at(0) > 96 && name.at(0) < 123)
{
insert(name, grade);
cout << name << " " << grade << endl;
}
}
myfile.close();
should the .close be part of an "else" statement with the if? a friend of mine has this exact thing but his does not abort.

First point, change your loop to something like:
while (getline(myfile, line)) {
// ...
}
Second, it's probably a lot simpler to feed the whole line to the stringstream, and read both pieces from there:
stringstream convert(line);
std::getline(convert, name, '\t');
convert >> grade;
Third, if you want to check for lower-case letters, you're much better off with islower than comparisons to magic numbers like 96 and 123. I wouldn't do the check that way though. If you want to check for a blank line, it would be better to do that directly, with line.empty() immediately after you read it. Using that, you end up with something like:
while (getline(myfile, line))
if (!line.empty()) {
std::istringstream convert(line);
std::getline(convert(line, name);
convert >> grade;
insert(name, grade);
std::cout << name << " " << grade << "\n";
}
There is one more step beyond that though: since the name and grade are (apparently) related, I'd probably define a class for them, and write an extractor to get an object of that type from a stream:
class whatever {
std::string name;
int grade;
friend std::istream &operator>>(std::istream &is, whatever &w) {
std::getline(is, w.name, '\t');
is >> w.grade;
is.ignore(4096, '\n');
return is;
}
};
With that in place, we can read the data from the file quite a bit more simply:
whatever w;
while (myfile >> w) {
insert(w);
std::cout << w.name << " " << w.grade << "\n";
}
Note that in this case, we don't have to check for the blank lines explicitly at all -- we just check whether we could successfully read and convert a whatever from the stream, which will fail for a blank line.

Related

c++ getline() doesn't seem to operate correctly, Doesn't read the line till the end

I have tried using cin.ignore(); instead of the dummy variable, didn't help. content of the file looks like this:
D Name Surname
A Name2 Surname2
...
amount of the students is unknown.
...
output should look like this:
Name Surname class: D
Name2 Surname2 class: A
but mine output loos like this:
Name Class A
urname Class S
Name2 Class E
urname2 Class S
...
here is the code:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream file("students.txt");
if (!file.is_open())
{
cout << "file can't be opened!" << endl;
exit(-1);
}
char classs;
string name, dummy;
while(file >> classs >> name)
{
cin.get(classs);
getline(cin, dummy);
getline(cin, name);
cout << name << " " << "calass " << classs << endl;
}
}
It looks like you've tried all kinds of things to make this work, including reading from cin in the middle of your loop that reads from file.. Very strange. I suggest you throw out the entire loop and try something simple as follows:
You just read one line at a time. For each line you read, you put that into a string stream and read the values out of that. To keep it even simpler, just read a string for everything, even if it's supposed to be just one character.
// Note: requires <sstream>
string line;
while(getline(file, line))
{
istringstream iss(line);
string cls, name, surname;
if (iss >> cls >> name >> surname)
{
cout << name << " " << surname << " class: " << cls << endl;
}
}
This does assume that there are always exactly two names, and it seems like that might not be the case for your input.
As an alternative, you could try reading the single character, then ignoring whitespace, and then read the entire rest of line:
char cls;
string fullname;
while (getline(file >> cls >> std::ws, fullname))
{
cout << fullname << " class: " << cls << endl;
}
Note that the above is being a bit lazy, and as a result this might break if you encounter an empty line in your file. So, combining with the istringstream approach would be the way to go:
string line;
while (getline(file, line))
{
istringstream iss(line);
char cls;
string fullname;
if (iss >> cls >> std::ws >> fullname)
{
// Note you may also want to test cls is valid (e.g. isalpha(cls))
cout << fullname << " class: " << cls << endl;
}
}
In the end, if your input is line-based, then as a general preference you should read lines and then decide what to actually do with them.
You are using both getline() and >> for reading from the file stream, and you shouldn't be. Here is an example using just >>:
std::string cls, name, surname;
while(file >> cls >> name >> surname)
{
std::cout << name << " " << surname << " class: " << cls << std::endl;
}
I am assuming that each line of the file has exactly three elements - class, name and surname, as that is the example in the question. If that is not the case, then you'll really need to read the file's contents line by line using getline() and then parse each line using for example istringstream.
Also, you are using both file and cin and your data is only in the file, so you don't need to be using cin at all.

How to insert multiple data types of varying length into variables?

I need to insert the following values from a text file; however, I am a little confused on how to take in a name that may be of different length in C++ and spaced out differently. This is the Text file contents:
2.5 John Jones
4.0 Madonna
3.773 Ulysses S. Grant
I was thinking to have a loop that takes in a name then adds the subsequent string to the original name, but does C++ know how to do this and stop reading in when the data type changes? This is what I was thinking:
double gpa;
string name;
string temp;
while (file >> gpa >> name) {
while (file >> temp) {
name += " " + temp
}
}
You can use >> (stream extraction) for the double, and std::getline for the std::string, like this:
double gpa;
std::string name;
while (file >> gpa && std::getline(file, name)) {
std::cout << gpa << ":" << name << "\n";
}
I'd do this by defining a type Name that reads data as you want a name to be read, but overloading operator>> for that type. The operator>> for std::string stops reading at the first white space, but since you have a type that should include all data to the end of the line, you can define that type, and overload operator>> to act appropriately for that type:
class Name {
std::string content;
public:
friend std::istream &operator>>(std::istream &is, Name &n) {
return std::getline(is, n);
}
operator std::string() const { return content; }
};
With that, we can read the data about the way we'd like:
double gpa;
Name name;
while (file >> gpa >> name) {
// print them back out, formatted a bit differently:
std::cout << name << ": " << gpa << "\n";
}

How to ensure user inputs a first and last name when only prompting for input once on one line?

C++ beginner here. I am storing structs of playerData in a vector (referred to as the player bank). One member of the struct is the player's name (stored in the struct as 1 string like "Julio Jones"). While using my application, if the user inputs a name that is not in my program's premade bank of players, the user is prompted to add the new name to the player bank for future use. The name input code is:
std::string input{};
std::cout << "Enter the player " << m_name << " traded: ";
std::getline(std::cin, input);
At the end of using the program, the user can save any changes they have made to the player bank for future use. I did so by writing the vector to a txt file
if (startAnswer == "exit")
{
std::cout << "Exiting Trade Analzyer...\n";
std::ofstream myfile;
myfile.open("playerbank.txt");
for (int k = 0; k < allPlayers.size(); ++k)
{
myfile << allPlayers[k].playerName << ' ' << allPlayers[k].playerPosition << ' ' << allPlayers[k].playersTeam << ' ' << allPlayers[k].playerValue << '\n';
}
myfile.close();
return 0;
}
There are no issues at this point. However, the issue arises the next time the user tries to run the program and use their updated player bank: any new player they had previously entered into the bank that only had a first name will not be extracted from the txt file because I extract the values as follows:
std::istream& operator>> (std::istream& input, playerData& data)
{
std::string first;
std::string last;;
input >> first >> last;
input >> data.playerPosition;
input >> data.playersTeam;
input >> data.playerValue;
data.playerName = first + ' ' + last;
return input;
}
std::vector<playerData> createPlayerBankFromFile()
{
std::vector<playerData> playerBank{};
playerData playerStruct;
std::ifstream inFile;
inFile.open("playerbank.txt");
while (inFile >> playerStruct)
{
playerBank.push_back(playerStruct);
}
inFile.close();
return playerBank;
}
The issue is of course that I am trying to extract two strings, but there is only 1 when it encounters a new player struct that the user added to the player bank with only a first name. It seems to me that an easy solution to this would be to write a loop to ensure that the user inputs both a first and last name when creating new players, but how can I do so? I could do two extraction lines like:
std::string first;
std::string last;
std::cout << "Enter player's first name: ";
std::getline(std::cin, first);
std::cout << "Enter the player's last name: ";
std::getline(std::cin, last);
then concatenate the strings to make the full player name, but I'd like to prompt the user once and get the full player name in one line.
You are assuming that people have only 1 first name and 1 last name separated by 1 space. Names are not always that simple.
You should prompt the user for 1 string, save that to the playerName, and then store the entire playerName as-is to your file followed by a delimiter that is guaranteed to never appear in any name, whether that be a line break, or any other character you want (other than space), eg:
myfile.open("playerbank.txt");
for (int k = 0; k < allPlayers.size(); ++k)
{
myfile << allPlayers[k].playerName << '|' << ... << '\n';
}
myfile.close();
Then, when reading back a name from the file, you can use std::getline() to read directly into playerName (get rid of first and last completely) until your chosen delimiter is reached, eg:
#include <limits>
std::istream& operator>> (std::istream& input, playerData& data)
{
// read until the delimiter is reached...
std::getline(input, data.playerName, '|');
// read the other values from input as needed...
// don't forget to read and discard the trailing line break at the end!
input.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
return input;
}
Alternatively:
#include <sstream>
std::istream& operator>> (std::istream& input, playerData& data)
{
std::string line;
if (!std::getline(input, line)) //<-- handles the trailing line break for you
return input;
std::istringstream iss(line);
// read until the delimiter is reached...
std::getline(iss, data.playerName, '|');
// read the other values from iss as needed...
return input;
}

The program doesn't proceed inside the input file to read it

Those are the parts of the code I have:
ifstream inFile;
inFile.open("Product1.wrl");
...
if (!inFile.is_open()){
cout << "Could not open file to read" << endl;
return 0;
}
else
while(!inFile.eof()){
getline(inFile, line);
cout << line << endl; //this statement only to chech the info stored in "line" string
if (line.find("PointSet"))
inFile >> Point1;
}
The output shows me the same string over and over again. So this means that the cursor inside the file does not proceed and getline reads the same line.
What might be the problem of this odd behavior?
If this is relevant:
The file does open as a .txt file and contains the exact information I need.
Okay I figured the problem:
Even after first eteration the return value of line.find("PointSet")is: 429467295... while my line string contains only one letter "S". Why?
Change
while(!inFile.eof()){
getline(inFile, line);
to
while( getline(inFile, line) ) {
I don't know why people get bitten by eof() quite so often, but they do.
Mixing getline with >> is problematic, because the >> will leave a '\n' in the stream, so the next getline will come back empty. Change that to use getline as well.
if (line.find("PointSet")) isn't what you want either. find returns the position in the string, or std::string::npos if it wasn't found.
Also, you can change
ifstream inFile;
inFile.open("Product1.wrl");
to
ifstream inFile ("Product1.wrl");
Here's a version showing the reads:
class Point
{
public:
int i, j;
};
template <typename CharT>
std::basic_istream<CharT>& operator>>
(std::basic_istream<CharT>& is, Point& p)
{
is >> p.i >> p.j;
return is;
}
int main()
{
Point point1;
std::string line;
while(std::getline(std::cin, line))
{
std::cout << line << '\n'; //this statement only to chech the info stored in "line" string
if (line.find("PointSet") != std::string::npos)
{
std::string pointString;
if (std::getline(std::cin, pointString))
{
std::istringstream iss(pointString);
iss >> point1;
std::cout << "Got point " << point1.i << ", " << point1.j << '\n';
}
else
{
std::cout << "Uhoh, forget to provide a line with a PointSet!\n";
}
}
}
}

using sstream header file in C++

so I was trying to utilise the istringstream to parse through a text file. The idea is to break down each line by space and based on the substring do stuff. The code works fine except for two things, it double counts last substring for each line and it seg faults when its done reading through the file. I have not used sstream before so any insight would be helpful.
file.getline(str,80);
while(!file.eof())
{
cout<<str<<endl;
istringstream iss(str);
while (iss)
{
iss >> sstr;
cout << "Substring: " <<sstr << endl;
}
file.getline(str,80);
}
The while loops should go like this:
std::string line;
while (std::getline(file, line))
{
std::istringstream iss(line);
std::string token;
while (iss >> token)
{
cout << "Substring: " << token << endl;
}
}
The getline and input operations return the stream object, which itself has a specialized conversion to bool that indicates whether the operation succeeded, and it will fail precisely when you've reached the end of the respective stream.
while !eof is almost always wrong.
Switch to a different C++ book, and tell us which one you're using now so that we may mock and warn accordingly.
while (file.getline(str,80)) {
cout<<str<<endl;
istringstream iss(str);
while (iss >> sstr) {
cout << "Substring: " <<sstr << endl;
}
}