C++ discard the leftover newline from input buffer issue - c++

I am trying to get this assignment down for a c++ class. I had issues with the do/while loop not working correctly and someone suggested adding the line cin.ignore(2,'\n'); in the InputData function under where the enter student name is being asked by the user. That worked and the do/while is now working. However, I'm not 100% sure how the cin.ignore(2,'\n'); works and I have an issue during the first go around where the first two characters of the "name" that the user inputs is getting discarded. If I change that 2 to a 0 it it doesn't cut off the first two characters of the name but if the user enters 'y' they'd like to continue, the program skips the first question "Enter the name of the student".
Any help is greatly appreciated!!
FYI, I am super new to programming in general, especially c++. Be nice please lol.
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
class Student {
public:
Student();
~Student();
// Input all info of user
void InputData();
// Output class list
void OutputData();
// Reset class list
void ResetClasses();
Student& operator =(const Student& rightSide);
private:
string name;
string stuName;
int numbClasses;
string *classList;
};//end student class
//Initialize variables to empty and array to NULL
Student::Student() {
numbClasses = 0;
classList = NULL;
name = "";
}//end variable initialization
//Frees up any memory allocated to array.
Student::~Student() {
if (classList != NULL) {
delete [ ] classList;
}//end if
}//end free memory
//Delete the class list
void Student::ResetClasses() {
if (classList != NULL) {
delete [] classList;
classList = NULL;
}//end if block
numbClasses = 0;
}//end reset classes
Here is where the line cin.ignore(2,'\n'); is located
// Inputs info from user.
void Student::InputData() {
int i;
// Reset the class list in case method is called again and array isn't cleared
ResetClasses();
cout << "Enter student name." << endl;
//Discards the leftover newline from input buffer
cin.ignore(2,'\n');
getline(cin, name);
cout << "Enter number of classes." << endl;
cin >> numbClasses;
//Discards the leftover newline from input buffer
cin.ignore(2,'\n');
if (numbClasses > 0) {
// Construct array big enough to hold # of classes
classList = new string[numbClasses];
// Loop through the # classes, input name of each one into array
for (i = 0; i < numbClasses; i++) {
cout << "Enter name of class " << (i+1) << endl;
getline(cin, classList[i]);
}//end for loop
}//end if block
cout << endl;
}//end input data
Output data
//Output info entered by user.
void Student::OutputData() {
int i;
cout << "Name: " << name << endl;
cout << "Number of classes: " << numbClasses << endl;
for (i=0; i<numbClasses; i++) {
cout << " Class " << (i+1) << ":" << classList[i] << endl;
}//end for loop
cout << endl;
}//end Output data
//overload this operator so there aren't two references to same class list.
Student& Student::operator =(const Student& rightSide) {
int i;
// Erase list of classes
ResetClasses();
name = rightSide.name;
numbClasses = rightSide.numbClasses;
// Copy the list of classes
if (numbClasses > 0) {
classList = new string[numbClasses];
for (i=0; i<numbClasses; i++) {
classList[i] = rightSide.classList[i];
}//end for loop
}//end if block
return *this;
}//end overload
Main, where the do/while loop is located.
// main function
int main() {
char choice;
//Do/While loop to ask user if they'd like to continue or end program.
do {
// Test code with two student classes
Student s1, s2;
// Input for s1
s1.InputData();
cout << "Student 1's data:" << endl;
// Output for s1
s1.OutputData();
cout << endl;
s2 = s1;
cout << "Student 2's info after assignment from student 1:" << endl;
// Should output same info as student 1
s2.OutputData();
s1.ResetClasses();
cout << "Student 1's info after the reset:" << endl;
// Should have no classes
s1.OutputData();
cout << "Student 2's info, should still have original classes:" << endl;
// Should still have original classes
s2.OutputData();
cout << endl;
cout << "Would you like to continue? y/n" << endl;
cin >> choice;
} while(choice == 'y'); //end do/while
return 0;
}//end main

I recommend you remove all the cin.ignore(2,'\n'); statements and instead skip the whitespace (spaces and returns) immediately before you use std::getline(). You can do this with the std::ws manipulator: see: std::ws
So your std::getline() statements become:
getline(cin >> std::ws, name); // NOTE: >> std::ws skips whitespace
So like this:
// Inputs info from user.
void Student::InputData() {
int i;
// Reset the class list in case method is called again and array isn't cleared
ResetClasses();
cout << "Enter student name." << endl;
//Discards the leftover newline from input buffer
//cin.ignore(2,'\n');
getline(cin >> std::ws, name); // NOTE: >> std::ws skips whitespace
cout << "Enter number of classes." << endl;
cin >> numbClasses;
//Discards the leftover newline from input buffer
//cin.ignore(2,'\n');
if (numbClasses > 0) {
// Construct array big enough to hold # of classes
classList = new string[numbClasses];
// Loop through the # classes, input name of each one into array
for (i = 0; i < numbClasses; i++) {
cout << "Enter name of class " << (i+1) << endl;
getline(cin >> std::ws, classList[i]); // NOTE: >> std::ws skips whitespace
}//end for loop
}//end if block
cout << endl;
}//end input data

Related

How can I find in an array of objects the main actor I need

This is my task:
I have done half of my code, but I'm struggling because I'm a beginner in OOP and I'm not sure how I can find movie where main_actor is Angelina Jolie.
for (int i = 0; i < n; ++i)
{
string name;
int year;
string prod;
string actor;
cout << "\nenter the film name " ;
cin >> name;
cout << "\nenter the production year ";
cin >> year;
cout << "\nenter the producer name ";
cin >> prod;
cout << "\nenter the actor name ";
cin >> actor;
obs[i].SetName(name);
obs[i].SetYearP(year);
obs[i].SetProducer(prod);
obs[i].SetMaina(actor);
if (actor == "Angelina Jolie")
{
cout << "The movie who has main actor Angelina Jolie is" << name << endl;
} // Тhis is my attempt.
}
}
You need to make a function that loops over your array and checks the main-actor:
bool findFilm(Film* films, int numFilms, string actor)
{
bool found = false;
for (int i = 0; i< numFilms; i++) {
if(!actor.compare(0, films[i].GetyMaina().length(), films[i].GetyMaina()){
cout<<"Film "<<films[i].GetName()<<" has main actor "<<actor<<"\n";
found = true;
}
}
return found;
}
The first thing you should do is using C++ containers like std::vector, std::array instead of raw array. And of course, then you should fill them.
std::vector<Films> films;
std::array<Films, 100> films;
The seconds thing is, you should delete "Films() = default;" part. That declaration changes everything in C++.
After these changes, you will be able to use containers' template member functions and algorithm functions (like find(), find_if(), count() etc.) to get what you need.
#include <algorithm>
If you are not able to do these changes, simply you can do it by looping:
for(auto film : films){
//comparisons, if checks, returns
}
Please use getline() function for user input because cin >> name will save from name Angelina Jolie only Angelina. Because it is reading only whole words not including white space.
To use function getline() put this after #include<cstring>
#include <string>
So use getline like this :
cout << "\n enter the actor name ";
std::getline (std::cin,actor);
Another problem is that you need cin.ignore() between two inputs. Because you need to flush the newline character out of the buffer in between.
Before loop ask for data like this :
cout << "how many films ";
cin >> n;
cin.ignore();
In loop like this :
cout << "\n enter the film name ";
getline(cin, name);
cout << "\n enter the production year ";
cin.ignore();
cin >> year;
cout << "\n enter the producer name ";
cin.ignore();
getline(cin, prod);
cout << "\n enter the actor name ";
getline(cin, actor);
b) (put this function in your class in public section right after string GetMania() ):
static void FindFilm(Film arr[], int cntFilms, string actor)
{
for (int i = 0; i < cntFilms; i++)
{
if (arr[i].GetMaina() == "Angelina Jolie")
cout << "The movie who has main actor Angelina Jolie is" << arr[i].GetName() << endl;
}
}
And from main call it like this right after loop.
string actor = "Angelina Jolie";
Film::FindFilm(obs, n, actor);
Also it's better if you write escape sequence (or special character) for new line (\n) to the end of output message. Like this :
cout << "The name of movie: \n" << name;

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.

Call array storing string type from one while to another

How to fix the code? I can't use vectors. I need to be able to call the names for the courses from the first while to the second one and display them.
cout << "Please enter the number of classes"<< endl;//Number of classes for the while
cin >> nclass;
while (count <= nclass ) // while
{
//Information for the class
{
cout << "Please enter the course name for the class # "<< count << endl;
getline (cin, name);
string name;
string coursename[nclass];
for (int i = 0; i < nclass; i++) {
coursename[i] = name;
}
}
char choose;
cin >> choose;
while ( choose == 'B' || choose == 'b') {//Name the courses
for (int x = 0; x < nclass; x++){
cout << "Here is a list of all the courses: \n" << coursename[i] << endl;
}
return 0 ;
}
you are declaring coursename as local inside loop and then using it outside so you get a compile time error (coursename is undeclared identifier).
one question: what is the role of inner for-loop????!!!
you use a for loop inside while loop through which you are assigning all the elements the same value as the string name has!!!
so every time count increments the inner for loop assigns the new value of name after being assigned, to the all elements of coursename.
count is undefined! so declare it and initialize it to 1 or 0 and take this in mind.
you wrote to the outbounds of coursname: count <= nclss to correct it:
while(count < nclass)...
another important thing: clear the input buffer to make cin ready for the next input. with cin.ignore or sin.sync
cout << "Please enter the number of classes"<< endl;//Number of classes for the while
cin >> nclass;
string coursename[nclass];
int count = 0;
while (count < nclass ) // while
{
//Information for the class
string name;
cout << "Please enter the course name for the class # "<< count << endl;
cin.ignore(1, '\n');
getline (cin, name);
coursename[count] = name;
cin.ignore(1, '\n');
count++;
}
char choose;
cin >> choose;
while ( choose == 'B' || choose == 'b') {//Name the courses
for (int x = 0; x < nclass; x++){
cout << "Here is a list of all the courses: \n" << coursename[x] << endl;
}
This code works!
#include <iostream>
#include <string>
using namespace std;
int main()
{
int nclass = 0, count = 1, countn = 1;
string name[100];
cout << "Please enter the number of classes" << endl;
cin >> nclass;
while (count <= nclass) {
cout << "Please enter the course name for the class # " << count << endl;
cin >> name[count];
count++;
}
cout << "Here is a list of all the courses: " << endl;
while (countn <= nclass) {
cout << name[countn] << endl;
countn++;
}
return 0;
}
Note that gave the array "name" the size of 100. Nobody is going to have 100 classes! There is no need for the for loops. It is a good practice to initialize the count and the new count which is designated by countn. Why is my answer voted down when it works?

C++ do/while loop and calling functions?

I am having trouble trying to get this program to loop if user enter 'y' they'd like to continue. I'm super new to programming by the way so any help is greatly appreciated. I figured the best was to add a do/while in main and as k the user if they'd like to continue at the end of the code but, I quickly realized that wouldn't work unless I called the previous methods for user input and output. That's where the issue is arising.
Thanks again for any help!
/*
• Ask the user if they want to enter the data again (y/n).
• If ’n’, then the program ends, otherwise it should clear the student class object and
repeat the loop (ask the user to enter new data...).
*/
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
class Student {
public:
Student();
~Student();
void InputData(); // Input all data from user
void OutputData(); // Output class list to console
void ResetClasses(); // Reset class list
Student& operator =(const Student& rightSide); // Assignment operator
private:
string name;
int numClasses;
string *classList;
};
//array intialized to NULL
Student::Student() {
numClasses = 0;
classList = NULL;
name = "";
}
//Frees up any memory of array
Student::~Student() {
if(classList != NULL) {
delete [] classList;
}
}
// This method deletes the class list
// ======================
void Student::ResetClasses() {
if(classList != NULL) {
delete [ ] classList;
classList = NULL;
}
numClasses = 0;
}
//inputs all data from user (i.e. number of classes)
//using an array to store classes
void Student::InputData() {
int i;
// Resets the class list in case the method
// was called again and array wasn't cleared
ResetClasses();
cout << "Enter student name." << endl;
getline(cin, name);
cout << "Enter number of classes." << endl;
cin >> numClasses;
cin.ignore(2,'\n'); // Discard extra newline
if (numClasses > 0) {
//array to hold number of classes
classList = new string[numClasses];
// Loops through # of classes, inputting name of each into array
for (i=0; i<numClasses; i++) {
cout << "Enter name of class " << (i+1) << endl;
getline(cin, classList[i]);
}
}
cout << endl;
}
// This method outputs the data entered by the user.
void Student::OutputData() {
int i;
cout << "Name: " << name << endl;
cout << "Number of classes: " << numClasses << endl;
for (i=0; i<numClasses; i++) {
cout << " Class " << (i+1) << ":" << classList[i] << endl;
}
cout << endl;
}
/*This method copies a new classlist to target of assignment. If the operator isn't overloaded there would be two references to the same class list.*/
Student& Student::operator =(const Student& rightSide) {
int i;
// Erases the list of classes
ResetClasses();
name = rightSide.name;
numClasses = rightSide.numClasses;
// Copies the list of classes
if (numClasses > 0) {
classList = new string[numClasses];
for (i=0; i<numClasses; i++) {
classList[i] = rightSide.classList[i];
}
}
return *this;
}
//main function
int main() {
char choice;
do {
// Test our code with two student classes
Student s1, s2;
s1.InputData(); // Input data for student 1
cout << "Student 1's data:" << endl;
s1.OutputData(); // Output data for student 1
cout << endl;
s2 = s1;
cout << "Student 2's data after assignment from student 1:" << endl;
s2.OutputData(); // Should output same data as for student 1
s1.ResetClasses();
cout << "Student 1's data after reset:" << endl;
s1.OutputData(); // Should have no classes
cout << "Student 2's data, should still have original classes:" << endl;
s2.OutputData(); // Should still have original classes
cout << endl;
cout << "Would you like to continue? y/n" << endl;
cin >> choice;
if(choice == 'y') {
void InputData(); // Input all data from user
void OutputData(); // Output class list to console
void ResetClasses(); // Reset class list
}
} while(choice == 'y');
return 0;
}
Just get rid of
if(choice == 'y') {
void InputData(); // Input all data from user
void OutputData(); // Output class list to console
void ResetClasses(); // Reset class list
}
Because the variables s1 and s2 are inside the do-while loop, they'll be recreated on each iteration. (The constructor will be called at the definition, and the destructor will be called at the closing brace of the loop, before it tests choice == 'y' and repeats).
The other problem you run into is that your standard input isn't in a state compatible with calling s1.InputData() again. Because you just used the >> extraction operator to read choice, parsing stopped at the first whitespace, and there is (at least) a newline leftover in the buffer. When Student::InputData calls getline, it will find that newline still in the buffer and not wait for additional input.
This is the same reason you used cin.ignore after reading numClasses. You'll want to do the same here.

Improper Data Reading from File (C++, fstream)

Entire question :
Question 3
You are the owner of a hardware store and need to keep an inventory that can tell you what different tools you have, how many of each you have on hand and the cost of each one. Write a program that initializes the random-access file "hardware.dat" to 100 empty records, let you input the data concerning each tool, enables you to list all your tools, lets you delete a record for a tool that you no longer have and lets you update any information in the file. The tool identification number should be the record number. Use the following information to start your file.
My Code :
int question_3()
{
cout << "Question 3" << endl;
fstream hardware;
hardware.open("hardware.dat" , ios::binary | ios::out);
//Create 100 blank objects---------------------------------------------------------------
if (!hardware)
{
cerr << "File could not be opened." << endl;
exit(1);
}
HardwareData myHardwareData;
for (int counter = 1; counter <= 100; counter++)
{
hardware.write(reinterpret_cast< const char * >(&myHardwareData), sizeof(HardwareData));
}
cout << "Successfully create 100 blank objects and write them into the file." << endl;
hardware.close();
hardware.open("hardware.dat" , ios::binary | ios::out | ios::in);
//Write data-----------------------------------------------------------------------------
int record;
int quantity;
float cost;
string tool_name;
cout << endl;
cout << "Enter record number (1 to 100, 0 to end input) : ";
cin >> record;
while (record != 0)
{
cin.sync();
cout << "Enter tool name : "; getline(cin, tool_name);
cout << "Enter quantity : "; cin >> quantity;
cout << "Enter cost : "; cin >> cost;
myHardwareData.setRecord(record);
myHardwareData.setToolName(tool_name);
myHardwareData.setQuantity(quantity);
myHardwareData.setCost(cost);
hardware.seekp((myHardwareData.getRecord() - 1) * sizeof(HardwareData));
hardware.write(reinterpret_cast<const char *>(&myHardwareData), sizeof(HardwareData));
cout << endl
<< "Enter record number (1 to 100, 0 to end input) : ";
cin >> record;
}
cout << "Successfully write all input data into the file." << endl;
//Read data----------------------------------------------------------------------------
cout << endl;
outputDataLineHead();
hardware.read(reinterpret_cast<char *>(&myHardwareData), sizeof(HardwareData));
int counter = 0;
cout << setprecision(2) << fixed;
while (hardware && !hardware.eof())
{
if (myHardwareData.getRecord() != 0)
outputDataLine(cout, myHardwareData);
hardware.seekp(counter++ * sizeof(HardwareData));
hardware.read(reinterpret_cast<char *>(&myHardwareData), sizeof(HardwareData));
}
return 0;
}
//Function for showing data in line form.-----------------------------------------------
void outputDataLineHead()
{
cout << left << setw(17) << "Record No."
<< left << setw(17) << "Tool Name"
<< left << setw(17) << "Quantity"
<< left << setw(17) << "Cost" << endl;
}
void outputDataLine(ostream &output, const HardwareData &Object_in_file)
{
output << left << setw(17) << Object_in_file.getRecord()
<< left << setw(17) << Object_in_file.getToolName()
<< left << setw(17) << Object_in_file.getQuantity()
<< left << setw(17) << Object_in_file.getCost() << endl;
}
HardwareData.h :
#ifndef HAREWAREDATA_H
#define HAREWAREDATA_H
#include <iostream>
using std::string;
class HardwareData
{
public :
HardwareData(string name = "", int recd = 0, int qutity = 0, float cot = 0.0)
{
setToolName(name);
setRecord(recd);
setQuantity(qutity);
setCost(cot);
}
void setToolName(string name)
{
const char *nameValue = name.data();
int length = 0;
length = (length < 15 ? length : 14);
strncpy(tool_name, nameValue, length);
tool_name[length] = '\n';
}
string getToolName() const
{
return tool_name;
}
void setRecord(int recd)
{
record = recd;
}
int getRecord() const
{
return record;
}
void setQuantity(int qutity)
{
quantity = qutity;
}
int getQuantity() const
{
return quantity;
}
void setCost(float cot)
{
cost = cot;
}
float getCost() const
{
return cost;
}
private :
char tool_name[15];
int record;
int quantity;
float cost;
};
#endif
I want to show the data like the following :
Record No. Tool Name Quantity Cost
4 electric hammer 3 34.32
How to achieve this?
Thank you for your attention.
I think your problem is while reading data.. Please check your variables if they get correct data or not.. You can check this with counting characters or try to printf them.
If they are not correct. You can use such an example which i used in below.
First of all i prefer you to read your line like this example ;
In this example i get coordinates of faces. You should change parameters.. In order not to read no need data
std::string str;
while(std::getline(in, str))
{
sscanf(str.c_str(), "%d %f %f", &fiducial.number, &fiducial.x, &fiducial.y);
coord_Num[fiducial.number] = fiducial.get_number();
coord_X[fiducial.number] = fiducial.get_x();
coord_Y[fiducial.number] = fiducial.get_y();
}
If everything looks fine. You should check
void outputDataLine(ostream &output, const HardwareData &Object_in_file)
The core issue here is that you're reading and writing bytes to/from objects of type HardwareData when rather you should be creating inserters/extractors so you can implement correct I/O semantics. For example:
// Inside HardwareData class
friend std::ostream& operator<<(std::ostream&, const HardwareData&);
friend std::istream& operator>>(std::istream&, HardwareData&);
These two declarations are for the inserter and extractor respectively. Input should consist of extracting into the record, tool_name, quantity and cost data members; and output should simply be an stream insertion which is trivial to implement.
It is often the problem when mixing formatted input with unformatted input that the residual newline inhibits further input. That seems to be the case here:
cin >> record; /*
^^^^^^^^^^^^^^ */
while (record != 0)
{
cin.sync();
cout << "Enter tool name : "; getline(cin, tool_name);
// ^^^^^^^^^^^^^^^^^^^^^^^^
// ...
}
After cin >> record; finishes, there will be a newline left inside the stream. That newline will stop std::getline() from working correctly because std::getline() only reads until the newline.
The fix here is to ignore this new line by using the std::ws manipulator:
std::getline(std::cin >> std::ws, tool_name);
// ^^^^^^^^^^^^^^^^^^^
Note: I talk about this in more detail here.
But this manual extraction isn't needed as we've already defined the inserter and extractor for our class. So all that's really needed is the following:
while (std::cin >> myHardwareData)
{
hardware << myHardwareData;
}
or
std::copy(std::istream_iterator<HardwareData>(std::cin),
std::istream_iterator<HardwareData>(),
std::ostream_iterator<HardwareData>(hardware));
Noticed how I've also taken out the check for a 0 value of record in the while loop. That's because the extractor takes care of it by reflecting a 0 value of record as invalid input. It sets the stream state of the stream if this occurs, thus allowing ourselves to be ejected from the while if that happens:
std::istream& operator>>(std::istream& is, HardwareData& hd)
{
cout << "Enter record number (1 to 100, 0 to end input) : ";
if ((is >> record) && record != 0)
{
// ...
} else
{
is.setstate(std::ios_base::failbit);
}
// ...
}
And the rest of your code be changed to:
std::cout << myHardwareData;
hardware >> myHardwareData;
std::cout << std::setprecision(2) << std::fixed;
while (hardware >> myHardwareData)
{
if (myHardwareData.getRecord() != 0)
std::cout << myHardwareData;
}
I don't really know what the seekps are for. If you elaborate on that, that would really help me adapt my code more accurately to your needs.