Why is my Library Management program not writing to file - c++

I am creating a library Management which you can add books, dvds and students to. The books and dvds can then be issued to the Students. The books, dvds and students are written to a .dat file.
The problem I am having is that the program doesn't seem to be writing the students to the .dat file and therefore when I search for a student or try to issue a book student it doesn't find the student, it gives me an error saying student not found. Can someone tell me what I am doing wrong.
I have included some of the code below.
Main.cpp
#include <iostream>
#include "Library.h"
int main() {
Library lib1;
while(1)
{
char studentOption;
char name[30];
std::cout<<"1 to add a new student\n";
std::cout<<"2 to search for a student\n";
std::cin.getline( name, 80);
studentOption = name[0];
switch(studentOption){
case '1':
lib1.insertStudent();
break;
case '2':
char regno[6];
std::cout<<"Enter the registration no. of the student you want to search: \n";
std::cin>>regno;
lib1.searchStudent(regno);
break;
}
};
return 0;
};
Library.h
#ifndef _Library_H_
#define _Library_H_
#include <stdio.h>
class Library
{
public:
void insertStudent();
void searchStudent(char regno[]);
};
#endif
Library.cpp
#include <iostream>
#include <fstream>
#include "Library.h"
#include "Student.h"
Student student1;
void Library::insertStudent()
{
std::fstream file;
file.open("student.dat",std::ios::out|std::ios::app);
student1.newStudent();
file.write((char*)&student1,sizeof(Student));
file.close();
}
void Library::searchStudent(char regno[])
{
std::fstream file;
int flag=0;
file.open("student.dat",std::ios::in);
while(file.read((char*)&student1,sizeof(Student)))
{
if((strcmp(student1.retregistrationNo(),regno)==0))
{
student1.displayStudent();
flag=1;
}
}
file.close();
if(flag==0)
std::cout<<"Error: Student not found in the system. \n";
}
Student.cpp
#include "Student.h"
void Student::newStudent()
{
std::cout<<"Enter the registration no. \n";
std::cin>>registrationno;
std::cin.ignore();
std::cout<<"Enter the name of the student \n";
fgets(name, sizeof(name), stdin);
stbookbar[0]='/0';
std::cout<<"Student added to system.\n";
}
void Student::displayStudent()
{
std::cout<<"Enter the registration no. : \n";
std::cin>>registrationno;
std::cin.ignore();
std::cout<<"Enter the name of the student: \n";
puts(name);
}
Student.h
#ifndef STUDENT_H_
#define STUDENT_H_
#include <iostream>
#include <fstream>
class Student
{
char registrationno[6];
char name[20];
char stbookbar[6];
char stdvdbar[6];
public:
void newStudent();
void displayStudent();
char* retregistrationNo()
{
return registrationno;
}
};
#endif

Although the following my not be the exact cause of your issue, they may lead to issues.
Use string not char*
There are soo many ways that a program can fail by using C-Style (char *) strings. There is also the issue of memory management.
Use std::string instead. If you need fixed record lengths, this can be handled at the point when you write the data. All other times you should be using std::string.
Also, you can use operator == with std::string instead of strcmp. The strcmp function will invoke undefined behavior if the two strings are not nul terminated.
Avoid fgets
There are buffer overflow and other issues with fgets. Use std::getline and std::string instead. This combination will handle expanding a string as required. It's safer.
Don't cross the streams
Be consistent, use C++ streams or C-style I/O, don't mix the two. For example, you use cout and in the same function, puts. There is a possibility that the two can use different I/O buffers. I don't see why you don't use cout << name;.
Use Boolean types, not 1 & 0
The C++ language has a type to represent true and false values. The type is bool, use it. The 1 and 0 representation of Boolean values is sooo old fashioned (like circa 1966). Move on to more readable code.
With integers, is the value 3 true or false? What about -1? From reading the code, there are a lot more reasons why an integer is set to 0 than setting a Boolean variable to false.
Readable code prevents many defects.
Use block statements, always.
All if, for, while, do-while should use blocks
Change your coding style to use '{' and '}' for single line and multi-line statements.
Avoid Magic Numbers
A magic number is a numeric constant without any description. Prefer named identifiers instead. Named identifiers make the code easier to read and easier to maintain.
For example, say you have a text field that has a capacity of 23 characters. If you change the capacity to 32, you will have to find every occurrence of 23, figure out if the instance applies to the text field, then modify them. With a named identifier, you would only make 1 change, and that is where the identifier is declared.
Use a debugger
Invest time now to learn to use a debugger. You'll find issues a lot faster on you own rather than having to wait for somebody to answer your post.
Use test cases
Design your program so it can read test cases. Place your data into a file and feed it to your program. Create test cases that pass. Create invalid test cases, these are the ones that will break your program. Can your program handle invalid input?
The test cases allow for automated testing your program. They also allow for consistent operation while you are debugging issues.
Edit 1: Comparing strings
Example:
std::string s1 = "Taco";
std::string s2 = "Salad";
if (s1 == s2)
{
cout << "s1 == s2\n";
}
if (s1 == "burrito")
{
cout << "s1 == burrito\n";
}

As #NathanOliver already pointed out in the comments, you need to open the file in binary mode in order to read bytes and put them in the student1 variable
file.open("student.dat",std::ios::in | std::ios::binary);
and correct the inner code of
displayStudent()
as it will not display the desired results

Related

How to read data from a file and store the names from the file in one vector, and the scores from the file in another?

My prompt asks:
In this program, we are going to input the name and score of 100 students from a file named
student.txt. This file has been provided to you. You have to use two vector variables, one to store the
student names, and another to store the student scores.
The text file referred to is formatted like:
James 80
Frank 67
Jenny 95
I'm struggling to understand how to read and store two variables from one file into two vectors, so if what I have so far doesn't make sense, I won't be totally shocked. There is an error message at my >> after infile saying there are no operators that match those operands and I have no idea what to do about that. Further, I just don't know where to go from here, or how to fix my current code. Any help is appreciated. Please be patient with me, I'm super new to this.
//Name
//This program will read and sort names and grades from a file using functions and vectors
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <vector>
using namespace std;
int main()
{
ifstream infile;
infile.open("student.txt");
if (infile.fail() == false)
{
vector<string> name;
vector<int> score;
while (infile >> name)
{
infile >> score;
}
}
else
{
cout << "Could not open the file." << endl;
}
return 0;
}
So I think you are failing to realise that you need to break the problem down into smaller steps. You (presumably) know how to read a single value from your file, and (presumably) know how to add a value to a vector. And you seem to realise that you need some kind of loop. What you have to do is put those techniques together to achieve the overall effect you want. Often when you do this you need to introduce variables to hold the intermediate values in your calculation. And that's the case here, we'll read the values from the file into variables and then we'll add the values in those variables to the vectors.
vector<string> all_names;
vector<int> all_scores;
string name;
int score;
while (infile >> name >> score) // read one name and one score
{
all_names.push_back(name); // add that name to vector
all_scores.push_back(score); // add that score to vector
}
The advice you got in the comments, that if a problem is too complex you should work first on a simpler version is very good advice as well. Many beginners when given a large or complex task try and solve it all at once. Professionals don't work that way, neither should beginners.

.txt file to an array of structures in C++?

I have previously used C++, but I can definitely say I am not a pro. Not really even at coding in general. I'm trying, as much as it may not look like it. :/
My task is to create an array of structres of the type, "book_list".
the struct looks like this:
struct book_list
{
int bookNumber; string bookTitle; string bookAuthor;
string bookDescription; int bookMonth; int bookYear; int bookRating;
};
It's easy enough to create the array itself:
book_list myBookList[50];
Here's where I'm having trouble:
The program is intended to search for a .txt file. If it finds it, it will read into the array of structures all the data contained within the file. So, bookNumber with the .txt file's bookNumber, etc.
I have been looking up potential solutions all day, and I've looked into ideas such as ifstream, getline, using iterators, etc. My research has been as scattered as my brain, and I think I am a little stumped.
Please help, and if you need any further info, please let me know!
Thanks,
-Jon
You need to either read/parse each entry separately or read the entire file in then parse it. Since you're storing all the structs in memory anyway, the latter is probably fine if you don't want to bother with reading one at a time, but for educational purposes, you might want to try reading one at a time. Maybe separate by lines?
Then you need a way to parse the entries. There's no one way to do this, but the easiest is probably to use a JSON-formatted text file, mainly because there are libraries that can use it and tools that can help visualize it. JSON lets you represent dictionaries, arrays, strings, numbers, and booleans. You can either do this manually without much effort since you don't have anything fancy like nested structures, or you can use a library like this to parse JSON for you: https://github.com/DaveGamble/cJSON
(which is in C, will have to link with C++ if you want to use C++)
Assuming that the question may be formulated as:
can you give me a tiny programming example that can kickstart my exercise:
program p:
#include <iostream>
using namespace std;
int main() {
int num;
string buf;
while (true) {
cin >> num; if (cin.eof()) break;
cin >> buf; if (cin.eof()) break;
cout << "num is: " << num << " buf is: " << buf << endl;
}
}
text file t.txt
1
one
2
two
you can run
p < t.txt
and see that the output is what you probably expected.
Now of course change cin >> num; into cin >> booklist[0].bookNumber; etc. and your exercise will be on the right track.
Use <stdio.h> for FILE.
char *name;
FILE *file;
name = "database.txt";
file = fopen(name,"w+");
book_list myBookList[50];
While writing use this.
book_list newRecord;
newRecord.bookNumber = 0;
//other assignments
fseek(file,0,SEEK_END);
fwrite(&newRecord,sizeof(book_list),1,file);
fclose(file);
While reading use below.
file = fopen(name,"r+");
book_list record;
fseek(file,0,SEEK_SET);
int i=0;
while(true){
fread(&record,sizeof(book_list),1,file);
if(feof(file))
break;
myBookList[i].bookNumber = record.bookNumber;
//other assignments
i++;
}
fclose(file);
Note: I'm not sure this is working properly with string class.I have used this with char[SIZE] and strcpy. My suggestion is try this first with built-in types like int, float, char etc.

read and extract informations from file c++

I am trying to find a solution for the error that the c++ compiler (codeblocks) keeps showing to me,I searched for answers on the net but none of them seemed to be helpful.
I have a txt file in which there are numbers,each of them is written in a line.
What i want is to read each number(line by line)and store it in a variable after converting it from string to float.
This my code
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
float z;
int j=0;
stringstream s;
const ifstream fich1;
fich1.open("test.txt",ios::in);
if(!fich1)
{
cerr<<"could not open the file";
EXIT_FAILURE;
};
else const string ligne1;
while((!fich1.eof())
{
if(j!=i)
{
j++;
getline(fich1,l1); // the first error is: no matching function for call to ‘getline(const ifstream&, const string&)
else if(j == i)
{
fich1>>s; // the second error is: ambiguous overload for ‘operator>>’ (operand types are ‘const ifstream {aka const std::basic_ifstream<char>}’ and ‘std::stringstream {aka std::basic_stringstream<char>}’)
z=(float)s; // I couldn't find a clear answer to convert from string to float
}
}
if anyone wants to ask any question about the code to make it clearer may ask it please,I am waiting for your answers as well as your questions :)
After the edit, I am able to read some code, but still I am suggesting the example I have below, since I am see scary things, like an EOF inside a loop!
Oh also, if you have C++11 support, then you could use std::stof.
You could try this (since your post is not readable), which reads a file line by line and stores every float number in a vector:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstdlib>
int main() {
std::vector<float> v;
std::string rec_file = "test.txt";
std::string line;
std::ifstream myfile(rec_file.c_str());
if(myfile.is_open()) {
while(std::getline(myfile,line)) {
if(!line.empty())
v.push_back(atof(line.c_str()));
}
myfile.close();
} else {
std::cerr << "Unable to open file";
}
for(unsigned int i = 0; i < v.size(); ++i)
std::cout << v[i] << std::endl;
return 0;
}
with the test.txt to be:
1.2
2.3
3.4
4.5
5.6
6.7
and the output:
1.2
2.3
3.4
4.5
5.6
6.7
Your ifstream shouldn't be constant, as getline alters the ifstream.
To convert from char array to float use atof(chararray)
To convert from string to float you could use atof(string.cstr())
You cannot use const here. But that's not your only problem.
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
Looks good.
float z;
int j=0;
stringstream s;
There should be some int main() { here, because that is the function called by the runtime when a C++ executable starts. This isn't Bash or Perl where execution simply picks up at the first statement.
And you either need to write using namespace std; or prefix identifiers like stringstream as std::stringstream.
const ifstream fich1;
This won't work. ifstream must not be declared const. (This is actually the explanation for the errors you encountered, but you made many more.)
An input file stream is an object with complex inner state, like where in the file you're currently at, error flags etc.; this inner state is changing so const ifstream simply won't work.
fich1.open("test.txt",ios::in);
This can't work either due to const, just like all the other operations on fich1 you're doing further down.
if(!fich1)
{ cerr<<"could not open the file";
EXIT_FAILURE;
};
EXIT_FAILURE is a symbolic constant for a return value. As-is, you could just as well have written 0;. If you expected this to end your program, you're wrong.
;
The semicolon ends the if.
else
Since the if has ended, this is a syntax error.
const string ligne1;
If you declare a string const, you cannot assign to it. Besides, even if the else were correct, that semicolon would have ended the else, because you didn't add braces (as you should always do, even with one-line blocks, because it's so easy to make mistakes otherwise).
The way the code continues, I very much doubt this was your intention. (Some discipline with the indenting makes this kind of mistakes really easy to spot.)
while((!fich1.eof())
How to read until EOF from cin.
{ if(j!=i)
{j++;
Do some proper indenting, will you? Besides, no i has been declared at this point.
getline(fich1,l1);
I assume l1 is the string you declared earlier as ligne1. (Since that happened inside the else, ligne1 is no longer in scope, but I'll let that one pass since you obviously intended the while to be inside the else block.)
Anyway, this can't work because both your ifstream and your string are constant, i.e. cannot be changed. This does not make sense, and thus getline() was not defined for const parameters.
else if(j == i)
Since you didn't close the brace of the if statement above, this is a syntax error as well. Again, proper indenting discipline would have made this immediately obvious.
{ fich1>>s;
I very much doubt there exists an operator>>() for const ifstream, so I am not surprised you get an error here.
z=(float)s;
You're trying to cast (C style, too...) a stringstream object to a float number? What do you expect the result might be? It will definitely not be the 3.14 or whatever you've written into test.txt...
}
}
Lost track of your braces. This code is FUBAR, I didn't even check if it makes sense semantically once all the errors are fixed, and I suspect you're pulling some elaborate troll prank here. If that isn't the case, I suggest you take another good look at whatever textbook you're using to learn C++, because you got a good many things very wrong.
Purely style-related advice: fich1, ligne1... don't use localized (non-English) identifiers. It just adds another problem when communicating about your code. (And this is coming from a non-native English speaker.)

Comparison with string literal C++

I'm writing a function for a program that allows a student to copy a template text file. This function checks the user's input to see if his desired template is allowed for his class.
I'm getting the error "Comparison with string literal results in unspecified behavior" on lines 21 and 25. I have done "cout << name" to verify that variable is storing correctly, which it is, so I know that's not the problem.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
//TEMPLATE CHECK
//First you check to see if the student is allowed to use the template
int templateCheck()
{
//Declare file name variable
char name[256];
//Prompt for user input
cout << "Enter file name: ";
//Cin user input
cin >> name;
//Begin check
//CS221 is the first template you can't use
if(name == "/home/cs221Temp.txt")
cout << "You are not allowed to use CS221 templates./n";
//CS 321 is the other template you can't use
else if (name == "/home/cs321Temp.txt")
cout << "You are not allowed to use CS321 templates./n";
//Any others are okay (I commented these out since I'm just working on this function by itself)
//else
//copyTemplate();
return 0;
}
This statement
if(name == "/home/cs221Temp.txt")
compares for pointers being equal (which is unlikely), not their contents.
What you actually want is
if(strncmp(name,"/home/cs221Temp.txt",256) == 0)
or
std::string name;
in your function.
You can't compare two C-style strings by ==ing them. (C-style string literals just give you a pointer to the first character in a sequence of characters in RAM, ended by a 0 valued character, so you'd be comparing addresses instead of strings).
What you want to use would be the strcmp function from stdlib.
However, you're writing C++, not C.
So, I recommend using the string class, which has an overloaded == operator, so you can do
if (string1 == string2)
In C/C++ (unlike in "similar" languages like JavaScript) when you use == on "strings" you are comparing pointers.
If you want to compare the strings' content, then you must use functions that are designed for that purpose. Like strcmp() from the standard C library

How to name text file at a certain directory?

Ok thanks for the answer Wug! I changed my code but now it's complaining about:
no matching function for call to
std::basic_ofstream::basic_ofstream(std::basic_string)
I'm not sure it makes any difference but i'll just post all of my code it's not that much so far.
I'll try to keep it cleaner from now on.
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <fstream>
using namespace std;
int main()
{ /*Introduction*/
SetConsoleTitle("Journal");
string action, prom0, filename, filepath;
filepath = "C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs\\";
cout << "Hi and welcome to Journal! \nHere you can write down your day.\nWrite help for";
cout << "more \nType command to start: ";
/*Choose Action*/
cin >> action;
if (action == "new")
{system("cls");
/*Make new Journal file*/
cout << "Filename: ";
getline(cin, filename);
mkdir("C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs");
ofstream journallogs(filepath + filename);
journallogs.close();
}
else {
cout << "Wrong command\n";
};
return 0;}
There are 2 things wrong. The first is what the compiler's complaining about:
ofstream journallogs("C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs\\" + getline(cin, filename), ios::out);
std::getline(istream&, string&) returns istream&, and you can't add char * to istream. I recommend taking a look at the documentation for getline(), which might help you understand better how you're supposed to use it. Here's an example anyway:
string filepath = "C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs\\";
string filename;
getline(cin, filename);
ofstream journallogs(filepath + filename);
The second problem is that you're reading from cin into filename before calling getline(). When you call getline(), any contents of filename are dropped, so you'll effectively trim the first word off of your filename, which probably isn't what you want. To fix that, remove the extraneous cin >> filename;
Note: indentation is important and helps you read your own code. Put forth the effort to keep your code looking nice.
First, learn this:
Start small and simple.
Add complexity a little at a time.
Test at every step.
develop new functionality in isolation.
Never add to code that doesn't work.
For the rest, I don't use Windows, so I can't be certain my code will work there, but the approach will.
You are trying to 1) get a filename from the user, 2) modify it and then 3) use it to open a file; we will develop these three things in isolation.
Getting a filename from the user. Civilized filenames do not contain whitespace, so they can be read with cin, but if you want to allow whitespace you can use getline instead. Either way, test it.
Modifying the filename. Write code that assigns a value to the filename, just as it does to the path-- do not get the filename from the user, it slows down your testing and is not proper isolation. Now try to append them. If you try filepath + filename, you may get a compiler error. Here's where you must understand the difference between std::string and char[]. A char[] is an array of char, and it (usually) contains a null-terminated sequence of characters; you must read up on arrays and pointers. It is a primitive type, and you cannot simply concatenate two of them with '+', you must use something like strcat, which is dangerous if you haven't done your homework on arrays. On the other hand, std::string is more sophisticated, and can handle '+' and many other operations. If you have a std::string x and you decide you want a char[] after all, you can get one like so: x.c_str().
Opening the file. If I remember right, the ofstream constructor can take a char[], but not a std::string. Test this with a hard-coded string (isolation!).
Once you have these three components working independently, you can hook them together.