C++ function and I/O file - c++

I'm trying to get the numbers of lines of input file to be size of array, but it keeps giving error that expression of the array must be constant. Here is my code:
int main() {
int count = 0;
string line;
ifstream infile("students.dat");
while (getline(infile, line))
count++;
cout << "Numbers of input is: " << count << endl;
const int size = count;
double max, min, average;
int freshman, sophomore, junior, senior;
string firstname[size];
string lastname[size];
string year[size];
double grade[size];
cout << "name\n";
for (int i = 0; i < size; i++) {
infile >> firstname[i] >> lastname[i]
>> year[i] >> grade[i];
}
max = FindMax(size, grade);
cout << "\nHighest grade: " << max;
min = FindMin(size, grade);
cout << "\nLowest grade: " << min;
average = FindAvg(size, grade);
cout << "\nClass average: " << average;
FindYear(size, year);
infile.close();
return 0;
}
Sample input file:
John Omi freshman 66
Katy Hill sophomore 55
Jeff Ozco freshman 90

constant means compile-time constant, which means the compiler would know its exact value at compile time. For example:
10 // constant
#define VAL 10
VAL // constant
const int val = 10 + 1;
val // constant
The size in your code is a variable that its value is not known until the program runs and reads your file, counts its line. It is called a run-time variable.

This here is wrong:
size_t size = count; // obtain the number of elements
string firstname[size]; //< Fails with a compiler error
The declaration something[<size>] tells the compiler to generate space for <size> elements something. Since you know the number of elements (count) only at runtime, the compiler does not know what to do. The size parameter must be a (fixed) number, when the compiler translates your source into binary code.
That said: Please learn the std library. std is part of the C++ language!
Don't ever use [] for vectors/arrays anymore (unless in very specific instances).
The std library has a whole lot of containers that are very easy to use. The most prominent container is std::vector.
Rewrite your code, for example like this:
std::vector<std::string> firstname;
firstname.resize(count);
for (...)
infile >> firstname.at(i);
Or better still:
struct student
{
std::string firstname;
std::string lastname;
...
};
...
std::vector<student> students;
while (!infile.eof()) {
std::string first, last, this, that;
infile >> first >> last >> this >> that;
students.emplace_back(first, last, this, that);
// or alternatively
students.push_back(student(first, last, this, that));
Like I said: The std library is part of the C++ standard.

Related

No error in code but the data is not being written in output txt [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I tried to write a code for my assignment. It is not showing any error but when I read input from the file, save them in array of object and then, I'm trying to put write values to an output file. the data is of that object which I want to write but nothing is being written in the output txt. I cant find the problem.
The input txt file format
2
//number of students
3
// number of grades (per student)
Student1 99 87 90
// / name grade grade grade
Student2 50 67 95
Code is written below
#include <iostream>
#include <conio.h>
#include <string>
#include <fstream>
using namespace std;
class Student
{
private:
string name;
int grade1;
int grade2;
int grade3;
float avg;
public:
Student()
{
name = "\0";
grade1 = grade2 = grade3 = avg = 0;
}
void setname(string Name)
{
name = Name;
}
void setgrade1(int num)
{
grade1 = num;
}
void setgrade2(int num)
{
grade2 = num;
}
void setgrade3(int num)
{
grade3 = num;
}
void AVG()
{
avg = (grade1 + grade2 + grade3) / 3;
}
float getAVG()
{
return avg;
}
string getName()
{
return name;
}
};
int main()
{
char ch = '\0';
char pre = '\0';
Student S[3];
int totalstudents = 0;
int totalgrades = 0;
cout << "Enter file name including " ", e.g \"myFile.txt\" :" << endl;
string file;
cin >> file;
fstream myFile;
int loop = 0;
int loop2 = 0;
myFile.open(file);
{
while (myFile.get(ch))
{
if (loop != 1)
{
myFile >> totalstudents >> totalgrades;
loop++;
}
string namE;
int g1, g2, g3;
myFile >> namE >> g1 >> g2 >> g3;
S[loop2].setname(namE);
S[loop2].setgrade1(g1);
S[loop2].setgrade2(g2);
S[loop2].setgrade3(g3);
S[loop2].AVG();
loop2++;
if (loop2 == totalstudents)
break;
}
}
myFile.close();
fstream myFile2;
myFile2.open("fout.txt");
{
for (int loop = 0; loop < totalstudents; loop++)
{
myFile << S[loop].getName() << "\t" << S[loop].getAVG() << endl;
}
};
myFile2.close();
_getch();
}
while (myFile.get(ch))
get() reads one character from the file. It is has now been read, never to be read again. Unfortunately, the subsequent code still expects the initial contents to be there, which it expects to read from this point on.
This is the fundamental logical error that fails to produce the expected results. You will need to completely redesign the input reading part of the problem, in order to fix the incorrect logic.
For some reason the current logic crams the code that reads the initial totalstudents and totalgrades as part of the loop that reads each student's data. This unnecessarily complicates the logic. Simply reading this, first, followed by a single loop to read each students data, should be sufficient. Unfortunately there are additional fundamental issues with the shown code that will require a more complete overhaul, in order to fix them properly.
The shown code can only handle no more than three students' data, and is hardcoded to handle exactly three grades for each student.
It seems clear to be from the description of the assignment that your program should handle any number of students, 2 students, 3 students, or a 100 students; and any number of grades per student, and not always three grades exactly. Otherwise there would be no reason, whatsoever, to provide these values in the input data.
Unfortunately, this is not possible using the current program's class, and logic, and this will also need to be addressed as well.

What is the correct syntax / format for entering information into the struct vector shown below

I am new to programming, and am trying to figure out one thing at a time.
What is the correct format for inputting information into the vector in line 13?
I am getting an error of StudentInformation has no member studentNames.
1 struct StudentInformation
2 {
3 std::string studentNames{};
4 int studentScores{};
5 };
6
7 void getInformation( int numberOfStudents, std::vector < StudentInformation> student )
8 {
9 for (int enterNameItterate{ 0 }; enterNameItterate = numberOfStudents; ++enterNameItterate)
10 {
11 std::cout << "Enter the name of the next student: ";
12 std::string studentName;
13 std::cin >> student.studentNames[enterNameItterate]{ studentName };
14 std::cout << "Enter the score of student: " << student.studentNames[enterNameItterate]{ studentName } << ": ";
15 int studentScore;
16 std::cin >> student.studentScores[enterNameItterate]{ studentScore };
17 }
18 }
You just want
std::cin >> student[enterNameItterate].studentNames;
If you dumb it down to just
std::string s;
std::cin >> s;
It might make it more obvious. The above code assumes you've already allocated the space for your vector, otherwise you would want this instead:
std::string studentName;
std::cin >> studentName;
int score = 0;
std::cin >> score;
student.emplace_back({studentName, score});
Something else that will quickly bite you on the bum: you're passing your vector into getInformation as a copy which means when you alter it within the function, you're not altering the actual object, but a copy of it. You can solve this by returning the vector of inputted information, or taking the vector by reference. Either:
std::vector<StudentInformation> getInformation(int numberOfStudents);
OR
void getInformation(int numberOfStudents, std::vector<StudentInformation>& student); // notice the &
I would change your structure slightly to provide a handy constructor function that takes a name and a score, e.g.
struct StudentInformation
{
StudentInformation() = default;
StudentInformation(const std::string& name, int score)
: name(name), score(score) {}
// renamed vars to something nicer.
std::string name{};
int score{};
};
I would change your function arguments slightly, so that you take a reference to an array of students. (Notice the '&' in the argument?)
void getInformation( int numberOfStudents, std::vector<StudentInformation>& students )
{
// simplifying the naming here. 'i' is generally known to be a counter.
for (int i = 0; i = numberOfStudents; ++i)
{
// some temp vars to store entered fields
std::string studentName;
int studentScore;
// get the data from the user
std::cout << "Enter the name of the next student: ";
std::cin >> studentName;
std::cout << "Enter the score of student: " << studentName << ": ";
std::cin >> studentScore;
// construct a new object at the end of the vector.
// this will call the constructor that takes two args
students.emplace_back(studentName, studentScore);
}
}

Array- looping through secondary array

Unable to get the index from the second array to match the first array
// array constants
const int people = 7;
const int phoneNumbers = 7;
int main() {
char person, family;
int index;
string found;
int size =7;
//array declaration and set values
string people [] ={"Darryl","Jasmine","Brian","Duane","Ayana","Mia","Maya"};
// const char phoneNumbers = 7;
string phoneNumbers[] = {"678-281-7649", "818-933-1158", "212-898-2022",
"361-345-3782","817-399-3750","313-589-0460","818-634-4660"};
//set boolean value
found = "False";
//initialize index
index = 0;
// search variable and user input
cout << "who are you looking for? " << endl;
cin >> people[index];
for (index=0; index<=6; index--) {
if (people[index] == people[index] )
cout << "phone num for " << people[index] << " is "<<
phoneNumbers[index] << endl;
}
return 0;
}
When I put in Jasmine which is the people[] array, the phoneNumbers[] array brings back the first index of phoneNumbers[] which it should bring back the second index on phoneNumbers[] array
There are two problems here.
You are replacing content of the people[0] with the cin - so first item of the array will be always user's input.
You are decrement index, so the FOR cycle goes to the indexes 0,-1,-2... This is problem as arrays in C and C++ goes from 0 to upper values.
I would prefer to add one variable:
string input;
then:
cin >> input;
for cycle should be used:
for (index = 0; index < 6; index++)
and in your condition I would use:
if (person[index] == input) ...
A much cleaner way of doing this would be too use std::map provided by the C++ standard template library. Below is my take on what you're trying to achieve:
// phoneNumber.cpp
#include <iostream>
#include <map>
int main()
{
// delcare a map variable that maps people's names to phone numbers
std::map <std::string,std::string> lookUpPhoneNumber = {
{"Darryl", "678-281-7649"},
{"Jasmine", "818-933-1158"},
{"Brian", "212-898-2022"},
{"Duane", "361-345-3782"},
{"Ayana", "817-399-3750"},
{"Mia", "313-589-0460"},
{"Maya", "818-634-4660"}
};
// take input name
std::string inputName;
// user prompt
std::cout << "who are you looking for?: ";
std::cin >> inputName;
// look up name in map
if(lookUpPhoneNumber.find(inputName) != lookUpPhoneNumber.end()){
std::cout << "Phone num for " << inputName << " is: " << lookUpPhoneNumber[inputName] << "\n";
} else{
std::cout << "Name does not exist!\n";
}
return 0;
}
To compile and run, use the following command:
g++ -std=c++11 -o phoneNumber phoneNumber.cpp
./phoneNumber
Or check your compiler option to enable compiling with c++11 standard or higher.

Search through a 2D array and return the entire row

I am new to C++ and I am having a hard time figuring out how 2D arrays work.
I am making a program that takes a .txt file filled with "driver's license records" and reads them in, and then lets the user search through the "database" for for last name, age, or whether the driver is registered to vote (y/n).
A sample .txt file would look like this:
4
Chris Jones 19 Y 374122
Pat Smith 23 N 863901
Kyle Howard 31 Y 673911
Samantha Pratter 27 Y 874309
My main method is simply
int main(int argc, char* argv[])
{
Executive exec(argv[1]);
exec.run();
return (0);
}
Here is the code for my Executive class:
#include <iostream>
#include <fstream>
#include "Executive.h"
#include "DriversLicenseRecord.h"
using namespace std;
Executive::Executive(char* filename){
int n;
ifstream inp(filename);
inp >> n;
num_records=n;
DriversLicenseRecord** record = new DriversLicenseRecord*[n];
for(int i=0; i<n; i++){
record[i] = new DriversLicenseRecord(inp);
}
}
//here is where I am pretty much guessing
void Executive::run(){
int x=0;
do{
cout << "1: Query last name" << endl << "2: Query age range" << endl << "3: Query registered voters" << endl << "4: Quit" << endl;
cin >> x;
if(x==1){
string name;
cout << "Enter last name: ";
cin >> name;
/**for(int i=0; i<num_records; i++){
if(name==record[i]){
cout << record[i];
}
}*/
}
else if(x==2){
int max;
int min;
cout << "Enter age range: " << endl << "min: ";
cin >> min;
cout << "max: ";
cin >> max;
}
else if(x==3){
}
}while(x!=4);
}
And here is my DriversLicenseRecord class:
#include "DriversLicenseRecord.h"
using namespace std;
DriversLicenseRecord::DriversLicenseRecord(ifstream& inp){
inp >> first_name;
inp >> last_name;
inp >> age;
inp >> vote;
inp >> license;
}
Firstly I want to know if I'm reading in the values correctly, it is my understanding that it skips reading in white space, so the DriversLicenseRecord should be getting the correct values.
Secondly I have no idea how to search through this, and return the whole row.
Below is an example of output with a given .txt file:
1. Query last name
2. Query age range
3. Query registered voters
4. Quit
3 // user input
Chris Jones 19 Y 374122
Kyle Howard 31 Y 673911
Samantha Pratter 27 Y 874309
Just a small push in the right direction would be very helpful, I've been struggling with this problem all week and haven't made much progress.
Thank you!
There are a couple errors in your code, but first I'd like to say that there is (1) no need for a 2D array here and (2) you don't create a 2D array in Executive::Executive(). For (1): all you need in this task is an one-dimensional array (or container) of DriversLicenseRecord objects. Then, you can query the fields of individual objects and compare their values to the query to search for specific records. For (2), what you have created is simply an one-dimensional array of pointers to DriversLicenseRecord objects.
Here is where the errors appear.
Firstly, the variable records is local to the constructor. Once the c'tor returns, records will be destroyed. You won't be able to access it outside the constructor. Also, the memory you allocated will be lost, creating a memory leak.
Secondly, while creating the array is correct, iteration is not. Here's how you can iterate over the array and query the fields:
for(int i=0; i < num_records; i++){
// note the -> : because we're using a pointer, not the object itself
if(name == m_records[i]->first_name){
cout << m_records[i]->first_name;
// or, if you define operator<<(istream&, const DriversLicenseRecord&):
cout << *(m_records[i]);
}
Finally, why you had to use a dynamic array. Thing is, you don't know the number of entries until you read the file, and you can't create a variable length array other than with new, except inside a function as a local variable, but then see #1: it's lost on function exit. However, you could create a dynamic array of records, not pointers to records. For that, you need to supply a default constructor to DriversLicenseRecord, and then simply fill in the fields on the fly from the file. (Not the syntax you used with DriversLicenseRecord::DriversLIcenseRecord(istream&), that's not the default c'tor.)
Next, this is how I would go about this problem, using STL containers and algorithms.
1. Switch to std::vector, which has the benefit of being safer and more convenient to use.
2. I honestly disliked the idea of creating the D.L.R. class with an istream parameter. What you can do, if you want to use streams, is to define istream& operator>>(istream&, DriverLicenseRecord&) and then use the beautiful STL syntax like so:
std::istream& operator>>(std::istream& str, DriversLicenseRecord& rec)
{
std::string str;
str >> rec.first_name >> rec.last_name >> rec.age >> temp >> rec.license;
rec.can_vote = (temp == "Y");
return str;
}
Then some STL beauty:
class Executive {
typedef std::istream_iterator<DriversLicenseRecord> in_driver_it;
std::vector<DriversLicenseRecord> records;
public:
Executive(const std::string& file)
{
std::ifstream inp(file);
int n; inp >> n;
std::copy(in_driver_it(inp), in_driver_it(), std::back_inserter(records));
}
};
Same fot the output.
Long story short: here is the complete sample code using the standard library, which is not the shortest but simple on the other hand. Running out of space!
You can avoid using the 2D array.
Approach 1
Use a vector<string> instead. Makes things much simpler to handle.
Read from the text file and store each line as a string in the vector.
Then when you are searching for a particular query string all you need to do is process each string in the vector.
So for reading from the input text file you would do something like this:
ifstream inpFile(myfile.txt);
string line;
vector<string> myInpFile;
while(getline(inpFile,line))
{
myInpFile.push_back(line);
}
I'll leave the implementation of the string search as an exercise for you. Take a look at how to process strings here
Approach 2
Alternatively you can just read off what you need straight off the file into a string and then search the string. You wouldn't need DriversLicenseRecord in memory at all. But that's not what your TA appears to be looking for.

Creating Structures via fstream

One of the exercises in C++ Primer Plus is having me use fstream to open a txt file and input data into a structure then output it. First line of the txt file is the number of "donors". The problem I seem to be having is that (I think) when I use "inFile >> value;" to retrieve the number and then allocate the structure via new, its expecting an int and its getting a string? Is this correct? What should I be doing differently?
//ch6 p278 exercise #9
#include <iostream>
#include <cstring>
#include <fstream>
#include <cstdlib>
using namespace std;
const int SIZE = 60;
struct contributions
{
char name[20];
double dollars;
};
char filename[20];
string donors;
bool donated;
int main()
{
char filename[SIZE];
cout << "Enter name of file to scan: ";
cin >> filename;
fstream inFile;
inFile.open(filename);
if(!inFile.is_open())
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
inFile >> donors;
contributions * ptr = new contributions[donors];
for(int h = 0; h < donors; h++)
{
inFile >> ptr[h].name);
inFile >> ptr[h].dollars);
}
//BEGIN OUTPUT OF STRUCTURES
cout << "GRAND PATRONS:\n";
for(int i = 0; i < donors; i++)
{
if(ptr[i].dollars >= 10000)
{
cout << ptr[i].name << " donated " << ptr[i].dollars << endl;
donated = true;
}
}
if (!donated) {cout << "none.\n";}
donated = false;
cout << "PATRONS:\n";
for(int i=0; i < donors; i++)
{
if(ptr[i].dollars < 10000)
{
cout << ptr[i].name << " donated " << ptr[i].dollars << endl;
donated = true;
}
}
if (!donated) {cout << "none.\n";}
delete ptr;
return 0;
}
What should I be doing differently?
Well, if the code is based on what C++ Primer Plus has taught you - use a different text book. I don't want to insult you, but the code is really poor. For example, has the book covered the std::string class?
I would recommend junking it, and getting Accelerated C++ by Koenig & Moo (two of the original C++ team) which will teach you modern, good, idiomatic C++.
You are most certainly correct, I assume the code does not compile? You can just change the donors variable to an integer. The input streams are overloaded to work with all built in types, ie. bool, double, int etc.
The following will work, assuming the next value is an integer:
int numDonors;
inFile >> numDonors;
Note that the change to an integer is also necessary for the 'for' loops to work correctly, you are currently comparing a string to an integer, there is no built-in/default behavior for this.
Edit: I also noticed that when you delete your array of contributors you do:
delete ptr;
However, this will only clear out the first dynamically allocated block. The correct way to delete an array of dynamically allocated objects is:
delete [] ptr;
Edit 2: Thanks for the sample, so the reason it is not working correctly is the fact that the names are first and last, and the stream operator '>>' breaks on whitespace. So what is essentially happening is you first attempt to read the name, the stream reads the first name only and stops. Then you try to read the donated value, this attempts to read the last name as the value and cannot convert to an double so it returns 0.
donors is a string, and you are trying to use it as an array size:
string donors;
//...
inFile >> donors;
contributions * ptr = new contributions[donors];
You should make donors an int. Also it's usually better to use std::vector<> instead of manually messing with pointers to raw arrays. That would look something like this:
std::vector<contributions> contribs;
for (...) {
contributions contrib;
// .. Some way read contrib from inFile
contribs.push_back(contrib);
}