Output does not produce using method and struct - c++

I try this code using a method, array, and struct, but it doesn't produce output at all.
I also use most types of include, but it still doesn't produce any output.
#include<conio.h>
#include<iostream>
#include<stream>
#include<string>
#include<stream>
using namespace std;
struct PetLabel {
string petname;
string pettype;
int petprice;
string petowner;
string phoneno;
};
PetLabel pet [10];
stringstream ss;
void DataPet();
void petPrice();
void findOwner();
int main(){
DataPet();
findOwner();
petPrice();
return 0;
}
void DataPet(){
ifstream infile;
infile.open("petSold.txt");
string cnvt[10];
for(int i = 0; i < 10; i++){
getline(infile, pet[i].petname, ',');
getline(infile, pet[i].pettype, ',');
getline(infile, cnvt[i], ',');
ss << cnvt[i];
ss >> pet[i].petprice;
getline(infile, pet[i].petowner, ',');
getline(infile, pet[i].phoneno);
ss.clear();
}
infile.close();
}
void findOwner(){
int chosen;
for (int i = 0; i < 10; i++){
if (pet[i].petname == "Uyasolu"){
i = chosen;
}
}
cout << "Owner name : " << pet[chosen].petowner << " Phone no : " << pet[chosen].phoneno << endl;
}
void petPrice(){
ofstream outfile("catSold.txt");
outfile << "The cat sold with price greater than RM1000" << endl;
for (int i = 0; i < 10; i++){
if (pet[i].petprice > 1000){
cout << pet[i].petname << "," << pet[i].pettype << "," << pet[i].petprice << "," << pet[i].petowner << "," << pet[i].phoneno << endl;
}
}
outfile.close();
}
the output that I get is:
Owner name : Phone no :
but I can't understand, because there is no syntax error at all.
Is there anything I can add, so I can get the right output?

This method is weird:
void findOwner(){
int chosen;
for (int i=0;i<10;i++){
if (pet[i].petname == "Uyasolu"){
i = chosen;
}
}
cout<<"Owner name : "<<pet[chosen].petowner<<" Phone no : "<<pet[chosen].phoneno<<endl;
}
I think you mean chosen = i instead.

findOwner() is coded wrong.
int chosen; is uninitialized before the loop is entered. Inside the loop, chosen is never updated (i is updated instead, which affects the looping). After the loop is done, chosen has an indeterminate value, so accessing pet[chosen] is undefined behavior.
It should look more like this instead:
void findOwner(){
int chosen = -1;
for (int i = 0; i < 10; i++){
if (pet[i].petname == "Uyasolu"){
chosen = i;
break;
}
}
if (chosen != -1)
cout << "Owner name : " << pet[chosen].petowner << " Phone no : " << pet[chosen].phoneno << endl;
else
cout << "Pet not found" << endl;
}
Alternatively:
void findOwner(){
for (int i = 0; i < 10; i++){
if (pet[i].petname == "Uyasolu"){
cout << "Owner name : " << pet[i].petowner << " Phone no : " << pet[i].phoneno << endl;
return;
}
}
cout << "Pet not found" << endl;
}
There are some other issues in your code, too:
don't use <conio.h>, its old, and you are not using anything from it anyway.
you have #include<stream> twice, and also <stream> should be <sstream>
string cnvt[10]; should be string cnvt; and then cnvt[i] should be cnvt. You don't need a whole array when you are converting only 1 string at a time.
ss.clear(); does not do what you think it does. You need to use ss.str("") instead. Or, simply move the declaration of ss inside the loop as a local variable so it is destroyed and recreated on each loop iteration.
petPrice() is creating an output catSold.txt file, but is not writing anything to it.

Related

Get only similar data within a c++ vector

I'm trying to get all the data from a .txt file that have a similar last value, as in the image.
for example, I go through and create a vector where I save in a file only the lines that have the same last value "1565514F" then when I find a different final value "1678721F" I create another file and so on infinitely, the first file would have the name "1565514F.txt" and would save in the same, or in another vector, data like
1001;2021-03-01;False;0;0;1565514F
1001;2021-03-02;False;0;0;1565514F
1002;2021-03-03;False;0;0;1565514F
1002;2021-03-04;False;0;0;1565514F
1003;2021-03-05;False;0;0;1565514F
1003;2021-03-06;False;0;0;1565514F
1004;2021-03-07;False;0;0;1565514F
another file would be called "1678721F.txt" and would have data like this
1006;2021-03-03;False;0;0;1678721F
1006;2021-03-04;False;0;0;1678721F
1001;2021-03-05;False;0;0;1678721F
1001;2021-03-06;False;0;0;1678721F
1004;2021-03-07;False;0;0;1678721F
1004;2021-03-08;False;0;0;1678721F
1003;2021-03-09;False;0;0;1678721F
the code that I have done is the following
// read file
ifstream archivoPlanificacion;
archivoPlanificacion.open("entrada/Planificacion.txt");
string linea;
vector<string> planificacionVector;
while(getline(archivoPlanificacion, linea))
{
planificacionVector.push_back(linea);
}
archivoPlanificacion.close();
for (int i = 0; i < planificacionVector.size(); i++) {
vector<string> planificacionSplitted = splits(planificacionVector[i], ';');
string planificacionFecha = planificacionSplitted[1];
string planificacionRut = planificacionSplitted[5];
cout << "Planificacion Rut: " << planificacionRut << endl;
if (planificacionRut == planificacionSplitted[5]){
vector<string> soloRutMismoUser;
soloRutMismoUser.push_back(planificacionSplitted[5]);
//imprimir vector soloRutMismoUser
for (int i = 0; i < soloRutMismoUser.size(); i++) {
cout << "soloRutMismoUser: " << soloRutMismoUser[i] << endl;
}
} else {
cout << "nuevo rut" << endl;
string aux = planificacionRut;
//crear archivo de salida
}
cout << "Planificacion Fechas: " << planificacionFecha << endl;
}
I don't know if there is a better way, but I asked because I was nesting many for, I would appreciate any help, I still can't separate the data with the "if", thanks
Modifying your loop
string currentCol5; // current 'rut'
vector<string> sameCol5; // collection of same 'rut' col5
for (int i = 0; i < planificacionVector.size(); i++) {
vector<string> planificacionSplitted = splits(planificacionVector[i], ';');
string planificacionFecha = planificacionSplitted[1];
string planificacionRut = planificacionSplitted[5];
cout << "Planificacion Rut: " << planificacionRut << endl;
if (currentCol5 == planificacionSplitted[5] || currentCol5.length() == 0){
sameCol5.push_back(planificacionSplitted[5]);
} else {
// write file here
for (int i = 0; i < sameCol5.size(); i++) {
cout << "soloRutMismoUser: " << sameCol5[i] << endl;
}
// start next set
currentCol5 = planificacionSplitted[5];
sameCol5.clear();
}
cout << "Planificacion Fechas: " << planificacionFecha << endl;
}
I have not tested any of this because I don't have the data structures or the file.

Why does it give me an error no "operator=="?

This line is an error and I don't know why. The compiler is telling me that the string array can't be converted to a string variable. The search is a string variable the user types in to look for names. And the names[count] is checking through an array of names.
string search;
string names[5]={};
for(int count=0; count<5; count++)
{
cout<<"Enter a name"<<endl;
cin>>names[count];
}
cout<<"Names entered"<<endl;
for(int count=0; count<5; count++)
{
cout<<names[count]<<endl;
cout<<"What name would you like to search for"<<endl;
cin>>search;
for(int count=0; count<5; count++)
{
if(names[count]=search)
{
cout<<search<<"is on array "<<count<<endl;
}
else
{
cout<<search<<"is not on the list"<<endl;
}
}
Is giving you this error because you are using the = assignment operator instead of the == comparison operator. Only use the former when assigning values to variables and the second for comparing variables in a condition.
I hope this helps:
https://www.geeksforgeeks.org/what-is-the-difference-between-assignment-and-equal-to-operators/
Have a good one and happy hacking!
Your question title mentions operator==, but there is no use of operator== anywhere in the code you have shown.
Your search logic is all wrong, though. First off, it is in the wrong place, it should not be inside the 2nd for loop at all, it needs to be moved up 1 level. And second, it is using the assignment operator= when it should be using the comparison operator== instead. And third, it is handling its output incorrectly.
Try something more like this instead:
string search;
string names[5];
for(int count = 0; count < 5; ++count)
{
cout << "Enter a name" << endl;
cin >> names[count];
}
cout << "Names entered" << endl;
for(int count = 0; count < 5; ++count)
{
cout << names[count] << endl;
}
cout << "What name would you like to search for" << endl;
cin >> search;
int found = -1;
for(int count = 0; count < 5; ++count)
{
if (names[count] == search)
{
found = count;
break;
}
}
if (found != -1)
{
cout << search << " is on array " << found << endl;
}
else
{
cout << search << " is not on the list" << endl;
}
/* alternatively:
#include <algorithm>
#include <iterator>
string *namesEnd = &names[5];
if (std::find(names, namesEnd, search) != namesEnd)
{
cout << search << " is on array " << std::distance(names, found) << endl;
}
else
{
cout << search << " is not on the list" << endl;
}
*/

Exception thrown at 0x5914F3BE (ucrtbased.dll)

I have some code that takes a list of names + double values from a .txt file and displays these in the command prompt. For this an array of structs is dynamically allocated. The code should know the size of the array based on the first value in the .txt file, which is then followed by the names and associated values. It should then display the list in two parts with names that have an associated double value higher than or equal to 10.000 listed first. If none of the values qualifies, it displays 'None' in the first half.
The program executes, but the debugger gives an exception and the output is not as expected.
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
struct donor
{
string name;
double contribution = 0;
};
int main()
{
string filename;
ifstream inFile;
cout << "Enter name of data file: ";
cin >> filename;
inFile.open(filename);
cin.clear();
if(!inFile.is_open())
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
cin.clear();
donor* dlist = new donor[amount];
int i;
while(inFile.good())
{
for(i = 0; i < amount; i++)
{
getline(inFile, dlist[i].name);
cin.clear();
inFile >> dlist[i].contribution;
cin.clear();
}
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons = false;
for(i = 0; i < amount; i++)
{
if(dlist[i].contribution >= 10000)
{
grandpatrons = true;
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
if(grandpatrons == false)
{
cout << "None" << endl;
}
cout << "Here's the list of Patrons:\n";
for (i = 0; 1 < amount; i++)
{
if (dlist[i].contribution < 10000)
{
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
delete[] dlist;
return 0;
}
The donorlist.txt file looks like this:
4
Bob
400
Alice
11000
But the output looks like this:
Enter name of data file: donorlist.txt
Here's the list of Grand Patrons:
None
Here's the list of Patrons:
0
0
0
0
The exception that the debugger gives me is:
Exception thrown at 0x5914F3BE (ucrtbased.dll) in 6_9.exe: 0xC0000005: Access violation reading location 0xA519E363.
Now I assume something is going wrong with reading from the dynamically allocated memory. Maybe something is causing me to read from memory beyond the allocated array? I'm having trouble finding exactly where the mistake is being made.
Your problems begin with the wrong amount written in your data file.
Fix it with:
2
Bob
400
Alice
11000
They then continue with the fact that you inccorectly read the file.
Remember: Mixing operator>> and getline() is not as simple as it seems.
You see, operator>> IGNORES newline and space characters until it finds any other character.
It then reads the upcoming characters until it encounters the next newline or space character, BUT DOES NOT DISCARD IT.
Here is where the problem with getline comes in. getline reads EVERYTHING until it encounters newline or a specified delim character.
Meaning, that if your operator>> stops after encountering newline, getline will read NOTHING since it immediately encounters newline.
To fix this, you need to dispose of the newline character.
You can do this by first checking if the next character in the stream is indeed newline and then using istream::ignore() on it;
int next_char = stream.peek();
if(next_char == '\n'){
stream.ignore();
}
A working example of your code would be:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Suggestion: class/struct names should start with a capital letter.
struct Donor{
//Suggestion: Use member initializer lists to specify default values.
Donor() : name(), contribution(0){}
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
//Suggestion: Open the file immediately with the filename and use `operator bool` to check if it opened.
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount; //! Leaves '\n'
Donor* donors = new Donor[amount];
for(int i = 0; i < amount; ++i){
switch(inFile.peek()){
case '\n': inFile.ignore();
break;
case EOF: cout << "Donor amount too big!\n";
exit(EXIT_FAILURE);
}
getline(inFile, donors[i].name);
inFile >> donors[i].contribution;
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons_exist = false;
for(int i = 0; i < amount; ++i){
if(donors[i].contribution >= 10000){
grandpatrons_exist = true;
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
if(!grandpatrons_exist){
cout << "None\n";
}
cout << "Here's the list of Patrons:\n";
for(int i = 0; 1 < amount; ++i){
if(donors[i].contribution < 10000){
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
delete[] donors;
return 0;
}
Now, an even better solution would be to use vectors instead of raw pointers and implement operator>> and operator<< which would greatly simplify
the reading and printing of the objects.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Donor{
public:
Donor() noexcept: name(), contribution(0){}
friend istream& operator>>(istream& stream, Donor& donor){
switch(stream.peek()){
case EOF: return stream;
case '\n': stream.ignore();
}
getline(stream, donor.name);
stream >> donor.contribution;
return stream;
}
friend ostream& operator<<(ostream& stream, const Donor& donor){
stream << donor.name << ' ' << donor.contribution;
return stream;
}
const string& get_name() const noexcept{
return name;
}
const double& get_contribution() const noexcept{
return contribution;
}
private:
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
vector<Donor> donors(amount);
//Read it as `for donor in donors`
for(Donor& donor : donors){
inFile >> donor;
}
//An STL function that takes a lambda as the thirs argument. You should read up on them if you haven't.
//I would prefer using this since it greatly improves readability.
//This isn't mandatory, your implementation of this part is good enough.
bool grandpatrons_exist = any_of(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(grandpatrons_exist){
for(const Donor& donor : donors){
if(donor.get_contribution() >= 10000){
cout << donor << '\n';
}
}
}
else{
cout << "None\n";
}
cout << "\nHere's the list of Patrons:\n";
for(const Donor& donor : donors){
if(donor.get_contribution() < 10000){
cout << donor << '\n';
}
}
return 0;
}
Some other great improvements would be:
Use partition to seperate great patrons from normal ones.
Use stream iterators to read the objects into the vector.
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
//Ignore the first line completely
inFile.ignore(numeric_limits<streamsize>::max(), '\n');
//Calls `operator>>` internally
vector<Donor> donors(istream_iterator<Donor>{inFile}, istream_iterator<Donor>{});
auto first_grand_patron = partition(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(first_grand_patron == begin(donors)){
cout << "None!\n";
}
for(auto patron = begin(donors); patron != first_grand_patron; ++patron){
cout << *patron << '\n';
}
cout << "\nHere's the list of Patrons:\n";
for(auto patron = first_grand_patron; patron != end(donors); ++patron){
cout << *patron << '\n';
}
return 0;
}
Now some general tips:
Struct/Class names should start with a capital letter.
Stop Using std::endl.
No need to cin.clear(). Cin is only used once and never again.
Use member-initializer lists.
Optionally use ++i instead of i++ in for loops to get used to the correct way of incrementing a variable unless needed otherwise.
bool grandpatrons is too much of an abstract name for a flag.
donors is a subjectively better name than short for donor list.

Stream not reading string properly

I am a university student and recently started programming on C++, I am currently having an issue reading in a string using a stringstream.
I am reading information from a .csv file and storing the information in a class, what is puzzling me the most is that my code works perfectly for the first line of my .csv file but not for the second.
This is my class:
class Player {
public:
string name;
int points = 0;
int gamesPlayed = 0;
int todaysPoints = 0;
int timesPresident = 0;
int timesScum = 0;
char playing = 'n';
};
This is how my .csv file is formatted:
Name;Points;Games Played;Times President;Times Scum
Grace;0;0;0;0
Gabriel;0;0;0;0
And this is how I am reading it:
int readInPlayersList(Player playersList[], string filename) {
listIn.open(filename);
string line;
istringstream fixedLine;
int i = 0;
int j;
getline(listIn, line); //first line doesn't matter
while (getline(listIn, line)) {
if (listIn.eof()) { break; }
j = 0;
while (line[j]) {
if (line[j] == ';') { //swapping ; by space to read in easily
line[j] = ' ';
}
++j;
}
fixedLine.str(line);
cout << "Before reading, line = " << line << endl;
fixedLine >> playersList[i].name >> playersList[i].points >> playersList[i].gamesPlayed >> playersList[i].timesPresident >> playersList[i].timesScum;
cout << "After readng -> " << playersList[i].name << " " << playersList[i].points << " " << playersList[i].gamesPlayed << " " << playersList[i].timesPresident << " " << playersList[i].timesScum << endl; //testing output
++i;
}
return i;
listIn.close();
}
As you can see, in order to find out what's wrong, I output what's in the line before reading into the class, and then I output the class members, for the first line, it reads it perfectly but not for the second and any other thereafter... See output below:
I really want to know what I am doing wrong here, appreciate any help, cheers!
Gabriel Groff

C++, Program ends before allowing input

To practice C++ I am trying to make a simple program that allows a user to input a name followed by a score and then allows the user to enter a name and get the score that name was entered with. The program works fine until I enter an escape character (ctrl + z) once I'm done entering names, after entering the escape character the program will output the line "Enter name of student to look up the score" but not allow the user to input the name and instead reads out "Press any key to exit". I'm totally stumped on how to fix this and any help is greatly appreciated.
#include "stdafx.h"
#include <std_lib_facilities.h>
int main()
{
vector <string>names;
vector <int>scores;
string n = " "; // name
int s = 0; // score
string student = " ";
cout << "Enter the name followed by the score. (Ex. John 89)" << endl;
while(cin >> n >> s)
{
for(size_t i = 0; i < names.size(); ++i)
{
if(n == names[i])
{
cout << "Error: Duplicate name, Overwriting" << endl;
names.erase(names.begin() + i);
scores.erase(scores.begin() + i);
}
}
names.push_back(n);
scores.push_back(s);
}
cout << "Name: Score:" << endl;
for(size_t j = 0; j < names.size(); ++j)
{
cout << names[j];
cout <<" " << scores[j] << endl;
}
cout << "Enter name of student to look up their score" << endl;
cin >> student;
for(size_t g = 0; g < names.size(); ++g)
{
if(student == names[g])
{
cout << "Score: " << scores[g] << endl;
}
}
keep_window_open();
return 0;
}
After you press the CTRL+Z key combination, which induces an EOF state to the cin stream, you need to bring the cin input stream back to its normal 'good' state to be able to use it again.
Add the following code after your for loop where you print the contents of the vectors.
cin.clear();
You may also check the state of the standard input stream using the rdstate() function. Anything other than 0 means that the standard stream is in an error state.
As has been said, you need to clear the error state on std::cin after reading the records failed.
std::cin.clear();
should do the trick. Here's my take on this using
proper data structures instead of two isolated vectors
const correctness
separating functions
no more hacky .erase() calls with magic indexes
#include <map>
#include <iostream>
std::map<std::string, int> read_records()
{
std::map<std::string, int> records;
std::string name;
int score;
std::cout << "Enter the name followed by the score. (Ex. John 89)" << std::endl;
while(std::cin >> name >> score)
{
if (records.find(name) != end(records))
{
std::cout << "Error: Duplicate name, Overwriting" << std::endl;
} else
{
records.insert({name, score});
}
}
std::cin.clear();
return records;
}
int main()
{
auto const records = read_records();
std::cout << "Name\tScore:" << std::endl;
for(auto& r : records)
std::cout << r.first << "\t" << r.second << std::endl;
std::cout << "Enter name of student to look up their score: " << std::flush;
std::string name;
if (std::cin >> name)
{
std::cout << "\nScore: " << records.at(name) << std::endl;
}
}
If you required contiguous storage, use a flat_map like the one from boost.