my text file was like
123456123456
Jason
uk
012456788
1000
456789456789
david
uk
012456788
1000
i'm trying to get the data from a text file and save it into arrays
however when i want to store the data from the text file into array it loop non-stop.
what should i do ?
the problem exiting in looping or the method i get the data from text file ?
code:
#include <iostream>
#include <fstream>
using namespace std;
typedef struct {
char acc_no[12];
char name[30];
char address[50];
char phone_no[12];
double balance;
} ACCOUNT;
//function prototype
void menu();
void read_data(ACCOUNT record[]);
int main() {
ACCOUNT record[31]; //Define array 'record' which have maximum size of 30
read_data(record);
}
//--------------------------------------------------------------------
void read_data(ACCOUNT record[]) {
ifstream openfile("list.txt"); //open text file
if (!openfile) {
cout << "Error opening input file\n";
return 0;
} else {
int loop = -1; //size of array
cout << "--------------Data From File--------------"<<endl;
while (!openfile.eof()) {
if (openfile.peek() == '\n')
openfile.ignore(256, '\n');
openfile.getline(record[++loop].acc_no, 12);
openfile.getline(record[loop].name, 30);
openfile.getline(record[loop].address, 50);
openfile.getline(record[loop].phone_no, 12);
openfile >> record[loop].balance;
}
openfile.close(); //close text file
for (int i = 0; i <= loop + 1; i++) {
cout << "Account " << endl;
cout << "Account No. : " << record[i].acc_no << endl;
cout << "Name : " << record[i].name << endl;
cout << "Address : " << record[i].address << endl;
cout << "Phone Number : " << record[i].phone_no << endl;
cout << "Balance : " << record[i].balance << endl;
}
}
}
UPDATE:
The OP didn't properly cite the correct format in his data file. This answer is only valid up until the last iteration.
Don't use .eof() - that's more applicable to when you want to open the file and read it by characters.
A better way would be to use the insertion operator >> as follows:
#define ARR_SIZE 31
ACCOUNT temp;
ACCOUNT record[ARR_SIZE];
int i=0;
while(i < ARR_SIZE) {
openfile >> temp.acc_no >> temp.name >> temp.address >> temp.phone_no >> temp.balance;
record[i] = temp;
i++;
}
Of course, even better is to use std::string to hold the values from the input file, in addition to using std::vectors instead of arrays.
Related
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.
I am currently doing an assignment for my beginner C++ course, the chapter is on structs. I am using visual studio so I am can't do anything fancy for dynamic array's (i.e. no vector's etc.).
The part of the homework I am having trouble with is reading a file with some spaces at the end of the file. Since I am using filename.eof() it is reading the blanks and recording that data. I tried doing cin.ignore(xxxxx, '\n'), however that did not work. The my current out put is the data I want but a row of garbage. How do I get rid of the garbage?
a) A function to read the data into the array. You can use the attached file named soccer-1.txt to test your code. It also goes without saying that your code must work with any input data file. Of course, for testing, use your file to avoid entering data while testing. The name of the data file must always be entered by the user (do not hard code a file name). Also, check to make sure that the given input data file exists. If the file does not exist, issue an error message to alert the user about the invalid file name. Make sure to ask the user again for the name of another file. However, terminate the program after the user has entered an incorrect file name for a total of 3 times. NOTE: the data file name can be input inside the function.
The text file looks like this:
"
Duckey E Donald forward 8 2 21
Goof B Goofy defense 12 0 82
Brave A Balto goalkeeper 0 0 5
Snow W White defense 1 2 3
Alice I Wonderful midfield 1 5 15
Samina S Akthar right_defense 1 2 7
Simba P Green left_back 7 3 28
**************WHITESPACE****************************
**************WHITESPACE****************************
Here is my code:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
const int subSize = 100;
//struct to store nameInfo
struct nameInfo
{
string fName;
char middleInitial;
string lName;
};
//struct to store playerInfo
struct playerInfo
{
nameInfo name;
string postion;
int goals;
int penalties;
int jersey;
};
int getData(playerInfo matrix[]);
void displayData(playerInfo matrix[], int arraySize);
int main()
{
playerInfo p;
playerInfo playerArray[subSize];
int arraySize;
int userSelection;
string searchTerm;
arraySize = getData(playerArray);
cout << arraySize << " records found." << endl << endl;
displayData(playerArray, arraySize); //call to display all data
cout << endl;
return 0;
}
//function to read the data into the array
int getData(playerInfo matrix[])
{
ifstream infile;
string fileName;
int i = 0; //counter to hold array row length
int k = 0; //counter for file input
int x = 0; //counter for user input
cout << "Enter the file name (e.g. soccer-1.txt): ";
getline(cin, fileName);
cout << endl;
infile.open(fileName.c_str());
//checks if file exists
//ask the user again for the name of another file
//loop returns -1 after 3 failed attempts to enter a file
while (!infile)
{
k++;
cout << "After attempt " << k
<< " input file not opened." << endl;
cout << "Attempt " << k + 1 << ", enter the file name (e.g. soccer.txt): ";
getline(cin, fileName);
cout << endl;
infile.open(fileName.c_str());
cout << endl;
if (k == 2) //terminate program at 2 because entered validation loop
{ //after first attempt
cout << "Terminating program.";
return -1;
}
}
while (!infile.eof())
{
infile >> matrix[i].name.fName >> matrix[i].name.middleInitial
>> matrix[i].name.lName >> matrix[i].postion
>> matrix[i].goals >> matrix[i].penalties
>> matrix[i].jersey;
i++; //holds size of array
}
infile.close();
return i;
}
void displayData(playerInfo matrix[], int arraySize)
{
for (int y = 0; y < arraySize; y++)
{
//display format:
//Duckey.(E)Donald:8 2 21 – forward
cout << matrix[y].name.fName
<< ".(" << matrix[y].name.middleInitial << ")"
<< matrix[y].name.lName << ":" << matrix[y].goals << " "
<< matrix[y].penalties << " " << matrix[y].jersey
<< " - " << matrix[y].postion << endl;
}
}
OK, this is where you can apply a change:
while (!infile.eof()) {
infile >> matrix[i].name.fName >> matrix[i].name.middleInitial >>
matrix[i].name.lName >> matrix[i].postion >> matrix[i].goals >>
matrix[i].penalties >> matrix[i].jersey;
i++; // holds size of array
}
Read to a string, or a set of strings, instead of straight to the container. That way you can check they're valid (i.e. not blank) before copying to the matrix container.
I wrote a program that dynamically allocates memory for an array of structure. It seems to work fine but when ever I try to delete the array the computer makes a "bong" noise and the program stops responding. It doesn't throw an error or anything, it just stops. I have tried delete and delete[] with the same result. I've tried moving the location of delete and found that I can delete it right after its created, but not after its been passed to any functions. Can anyone tell me why I can't delete memory?
#include <iostream>
#include <iomanip>
#include <fstream>
using std::cin;
using std::cout;
using std::endl;
using std::ifstream;
using std::ofstream;
using std::setw;
using std::left;
using std::fixed;
using std::setprecision;
//structure of an employee
struct Employee
{
char first_name[32];
char last_name[32];
char SSN[11];
float wage;
int hours;
char status;
};
void ReadData(Employee work_force[]);
void PrintData(Employee work_force[], int number_entries);
float CalculateStarightPay(Employee worker);
float CalculateOvertimePay(Employee worker);
int FindNumberEntries();
const int SPACING = 15; //spacing used in formating
const char dataFile[] = "EmployeeData.txt"; //file to read data from
const int union_dues = 5; //amount deducted from pay for the union
int main()
{
int number_entries = FindNumberEntries();
//array of employees
Employee * work_force = new Employee[number_entries];
//read in data
ReadData(work_force);
//prints the data
PrintData(work_force, number_entries);
//clean up memory
delete[] work_force;
return 0;
}
//finds the number of entries in the file
int FindNumberEntries()
{
int counter = 0;
//worker to read through file entries
Employee worker_temp;
ifstream input;
input.open(dataFile);
if (input.is_open())
{
while (!input.eof())
{
input >>
worker_temp.first_name >>
worker_temp.last_name >>
worker_temp.SSN >>
worker_temp.wage >>
worker_temp.hours >>
worker_temp.status;
cin.clear();
counter++;
}
input.close();
}
else
{
cout << "File could not be opened!" << endl;
}
return counter - 1;
}
//reads employee data from file
void ReadData(Employee work_force[])
{
ifstream input;
input.open(dataFile);
//reads in entries
if (input.is_open())
{
for (int i = 0; !input.eof(); i++)
{
//reads in employee data
input >>
work_force[i].first_name >>
work_force[i].last_name >>
work_force[i].SSN >>
work_force[i].wage >>
work_force[i].hours >>
work_force[i].status;
cin.clear();
}
input.close();
}
else
{
//error that file could not open
cout << "File could not be opened!" << endl;
}
}
//calculates straight pay
float CalculateStarightPay(Employee worker)
{
//determines of worker is fulltime or not
if (worker.status == 'F')
{
if (worker.hours > 40)
{
return ((worker.wage * 40) - 5);
}
else
{
return (worker.wage * worker.hours) - union_dues;
}
}
else
{
if (worker.hours > 40)
{
return (worker.wage * 40);
}
else
{
return worker.wage * worker.hours;
}
}
}
//calculate overtime pay
float CalculateOvertimePay(Employee worker)
{
//deermines if there are any overtime hours
if (worker.hours <= 40)
{
return 0;
}
else
{
//calculates overtime pay
return ((worker.hours - 40) * (worker.wage * 1.5));
}
}
//prints employee data in a well formated manner
void PrintData(Employee work_force[], int number_entries)
{
delete work_force;
float straight_pay = 0.0F;
float Overtime_pay = 0.0F;
char name[32] = { '\0' };
//print headers
cout << left <<
setw(SPACING) << "Name" <<
setw(SPACING) << "SSN" <<
setw(SPACING) << "Hourly Wage" <<
setw(SPACING) << "Hours Worked" <<
setw(SPACING) << "Straight Pay" <<
setw(SPACING) << "Overtime Pay" <<
setw(SPACING) << "Status" <<
setw(SPACING) << "Net Pay" << endl;
//prints data for each EMPLOYEE
for (int i = 0; i < number_entries; i++)
{
straight_pay = CalculateStarightPay(work_force[i]);
Overtime_pay = CalculateOvertimePay(work_force[i]);
//adds a space after first name
work_force[i].first_name[strlen(work_force[i].first_name) + 1] = '\0';
work_force[i].first_name[strlen(work_force[i].first_name)] = ' ';
//puts last name and first name together
strcpy(name, strcat(work_force[i].first_name, work_force[i].last_name));
//prints out all the data in a nic eformat
cout << fixed << setprecision(2) <<
setw(SPACING ) << name <<
setw(SPACING) << work_force[i].SSN << '$' <<
setw(SPACING) << work_force[i].wage <<
setw(SPACING) << work_force[i].hours << '$' <<
setw(SPACING) << straight_pay << '$' <<
setw(SPACING) << Overtime_pay <<
setw(SPACING) << work_force[i].status << '$' <<
setw(SPACING) << (straight_pay + Overtime_pay) << endl;
}
}
Don't delete work_force; at the top of PrintData.
Use std::strings for your names and SSN. (Fixed length strings are a usability accident waiting to happen).
Use a std::vector<Employee>. The important thing is that this means you no longer have to use new (which is something you should always try and avoid). It also means that you only have to read the file once - you just read the entry, and then push it onto the vector with push_back.
When reading from an input stream, you need to try to read and then test the stream to see if you have hit eof. So the read function would look like:
while (input >>
worker_temp.first_name >>
worker_temp.last_name >>
worker_temp.SSN >>
worker_temp.wage >>
worker_temp.hours >>
worker_temp.status)
{
workforce.push_back(worker_temp);
}
my text file was like
Jason Derulo
91 Western Road,xxxx,xxxx
1000
david beckham
91 Western Road,xxxx,xxxx
1000
i'm trying to get the data from a text file and save it into arrays however when i want to store the data from the text file into array it loop non-stop. what should i do ? the problem exiting in looping or the method i get the data from text file ?
code:
#include <iostream>
#include <fstream>
using namespace std;
typedef struct {
char name[30];
char address[50];
double balance;
} ACCOUNT;
//function prototype
void menu();
void read_data(ACCOUNT record[]);
int main() {
ACCOUNT record[31]; //Define array 'record' which have maximum size of 30
read_data(record);
}
//--------------------------------------------------------------------
void read_data(ACCOUNT record[]) {
ifstream openfile("list.txt"); //open text file
if (!openfile) {
cout << "Error opening input file\n";
return 0;
} else {
int loop = -1; //size of array
cout << "--------------Data From File--------------"<<endl;
while (!openfile.eof()) {
if (openfile.peek() == '\n')
openfile.ignore(256, '\n');
openfile.getline(record[loop].name, 30);
openfile.getline(record[loop].address, 50);
openfile >> record[loop].balance;
}
openfile.close(); //close text file
for (int i = 0; i <= loop + 1; i++) {
cout << "Account " << endl;
cout << "Name : " << record[i].name << endl;
cout << "Address : " << record[i].address << endl;
cout << "Balance : " << record[i].balance << endl;
}
}
}
Use ifstream::getline() instead of ifstream::eof() in tandem with >>. The following is an illustrative example, (and for simplicity I didn't check to see if the stream opened correctly).
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
#define ARR_SIZE 31
typedef struct {
char name[30];
char address[50];
double balance;
} ACCOUNT;
int main() {
ACCOUNT temp, record[ARR_SIZE];
ifstream ifile("list.txt");
int i=0;
double d=0;
while(i < ARR_SIZE) {
ifile.getline(temp.name, 30, '\n');//use the size of the array
ifile.getline(temp.address, 50, '\n');//same here
//consume the newline still in the stream:
if((ifile >> d).get()) { temp.balance = d; }
record[i] = temp;
i++;
}
for (int i=0; i < ARR_SIZE; i++) {
cout << record[i].name << "\n"
<< record[i].address << "\n"
<< record[i].balance << "\n\n";
}
return 0;
}
Another recommendation would be to use vectors for record array, and strings instead of char arrays.
REFERENCES:
Why does std::getline() skip input after a formatted extraction?
The program works all the way up until it checks for the name the user enters. When you enter the name you wish to search for in the array of structures that have been imported from a file full of customer info) it comes back segmentation fault core dumped. This puzzles me.
#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
using namespace std;
struct AccountsDataBase{
char name[50];
string email;
long int phone;
string address;
};
#define MAX 80
AccountsDataBase * account = new AccountsDataBase[MAX];
void readIn(ifstream& file){
int i=0;
while(!file.eof()){
file >> account[i].name >> account[i].email >> account[i].phone >> account[i].address;
}
}
void getAccount(){
char userPick[50];
char streamName[50];
cout << " What account will we be using? " << endl;
cin.getline(streamName, 50);
for(int i=0; strcmp(account[i].name, streamName)!=0; i++){
if( strcmp(account[i].name, streamName)==0){
cout << "\n\n FOUND IT!! \n\n";
cout << account[i].name << "\n" << account[i].email << "\n" << account[i].phone << "\n" << account[i].address << endl;
}
}
}
int main(){
ifstream file;
file.open("2.dat"); //opens data account records text
readIn(file);
getAccount();
delete account;
return 0;
}
Your loop keeps reading everything into the initial element of the array:
while(!file.eof()){
file >> account[i].name >> account[i].email >> account[i].phone >> account[i].address;
}
because the value of i is never incremented. You can convert this to a for loop, like this:
for (count = 0 ; count < MAX && !file.eof() ; count++) {
file >> account[count].name >> account[count].email >> account[count].phone >> account[count].address;
}
Note that I changed i to count:
AccountsDataBase * account = new AccountsDataBase[MAX];
int count = 0;
This will help you solve another problem - determining when the array ends in the getAccount function. Currently, you assume that the record is always there, so the outer loop keeps going on. Now that you have count, you could change the loop like this:
for(int i=0; i < count && strcmp(account[i].name, streamName)!=0; i++){
if( strcmp(account[i].name, streamName)==0){
cout << "\n\n FOUND IT!! \n\n";
cout << account[i].name << "\n" << account[i].email << "\n" << account[i].phone << "\n" << account[i].address << endl;
break;
}
}
if (i == count) {
cout << "Not found." << endl;
}