Alright so Here is the code I have currently for my delete function.
void classSchedule::deleteEntry(classSchedule schedule[], int numElems)
{
string entryToDelete;
cout << endl << "Enter the Teacher's Last Name of the Entry you want to delete: ";
cin >> entryToDelete;
int i, recordToDelete = -1;
for (i = 0; i < numElems; i++)
{
if (schedule[i].teacherLastName == entryToDelete)
recordToDelete = i;
}
if (recordToDelete != -1)
{
}
}
It essentially is like this: I have an array of classes that is called schedule[] each object has the following members: class department, class number, credit hours, teacher last name, teacher first name, room number, and current gpa. I started my function by asking them the teachers last name they want to delete, because all the rest could repeat. I searched my object for that last name.
I want to know how to delete the whole instance of that object from a file.
Is it possible for me to just overwrite it with the objects ahead or behind it.
i.e. they want to delete schedule[4] out of 10 instances could i just overwrite [4] with [5], [5] with [6] and so on?
Edit:
Here is the file that is being read wrote to(classes.txt):
ENG 112 3 Tetlof S S062 3.1
CST 280 3 Klingler T K114 3.4
LWA 220 2 Wesolak J M121 2.1
POL 103 4 Fortin B J122 4.1
ENG 111 2 Harood J K131 3.1
Edit2:
void classSchedule::outputToFile(classSchedule schedule[], int& numElems)
{
ofstream fileOut;
fileOut.open("classes.txt", ios::out | ios::app);
fileOut << schedule[numElems - 1].classDepartment
<< " " << schedule[numElems - 1].creditHours
<< " " << schedule[numElems - 1].teacherLastName
<< " " << schedule[numElems - 1].teacherFirstName
<< " " << schedule[numElems - 1].roomWingAndNumber
<< " " << schedule[numElems - 1].currentGPA;
fileOut.close();
}
You have three options: 1) Output the modified data to a new file; or 2) Mark records as deleted, but available for reuse; or 3) Move data "up" to the unused slots (like an array).
Maybe you should be using a database instead.
Many applications write the original and modified data to a new file. Data that is to be deleted is not written to the file. This is the simplest solution.
If your file has fixed sized records, you could mark the deleted record as available so it can be used again. This may require adding a flag to the record that indicates whether it is dead or alive.
An ugly method is to overwrite the deleted data with data that is forward of it. For example if I have a file containing 3 sentences and I delete sentence 2, I will read sentence 3 and write it to the position where sentence 2 started.
Related
We are doing an assignment with classes and objects and all that messy stuff.
I'm having a problem in which, when the user is done editing the array (adding or deleting data, blah blah blah), I need to update the .dat file with the values in the array for later use. I'm having a bit of trouble outputting the values into the file.
Any tips?
if this question has already been answered, just link me to the source and I'll delete this question.
ALSO, PLEASE NOTE THAT THE LIST ARRAY VARIABLE IN THE CODE IS FROM A CLASS.
Here is the test data (values are string, int, int, int, int, in that order):
MAN 5 9 2 89
LOL 4 3 8 23
SAP 7 2 2 34
And here is my code:
string name;
int gamesPlayed, goalsFor, goalsAgainst, points;
while (outfile << name << gamesPlayed << goalsFor << goalsAgainst << points)
{
for (int i = 0; i < numofclubs; i++)
{
outfile = list[i];
}
}
outfile.close();
You are not writing any delimiters between your values in the file. And, inside your for loop, you are trying to assign a value to the ofstream itself, rather than write the value to the file managed by the ofstream.
Also, you say the data is in an array, but you didn't show what that array actually looks like. Your while loop is not looping through any array. And your for loop is writing out club information that is not consistent with the desired file format you have described.
You probably need something more like this instead:
class Club
{
string name;
int gamesPlayed, goalsFor, goalsAgainst, points;
};
Club* list;
int numofclubs;
...
ofstream outfile("output.dat");
for (int i = 0; i < numofclubs; ++i)
{
outfile << list[i].name << ' '
<< list[i].gamesPlayed << ' '
<< list[i].goalsFor << ' '
<< list[i].goalsAgainst << ' '
<< list[i].points << '\n';
}
outfile.close();
First of I'd like to thank you all in advance for taking your time reading and helping me with my problem. I'm in no way shape or form an expert at c++, I'm not even good. I started programming in c++ 2 months ago and I find it quite harder than python, for a second experience with programming languages.
So I'm making this game for my programming class and I have to have a leaderboard text file with all the winners of a certain level. I set it up so the file always has the same format for time, name like this.
I've been trying to figure out how to sort the leaderboard entries by time and then by name. I thought of reading the file from line 3 and beyond but that doesn't seem to work. I moved on to what seems a better way of doing it which is to read the whole leaderboard discarding the first 2 lines, store it line by line on a vector, sorting the vector then and wiping the file by opening it in trunc mode but for some reason the file doesn't get wiped, it just keeps on adding more and more entries. I wan't it to add the sorted lines (vector) to the leaderboard one by one up until 10 entries are hit. Can someone help me? Here's a code sniped with the function I'm using to update the leaderboard
// Function to check if MAZE_XX_WINNERS.txt exists, if not creates it
void makeLeaderboard(string maze_name, string formated_time){
string winner_name, filename = maze_name.substr(0,7) +"_WINNERS.txt";
while(true){
// If MAZE_XX_WINNERS.txt file exists
if(ifstream(filename)){
// Open MAZE_XX_WINNERS.txt file in append mode
fstream leaderboard(filename, fstream::app);
// Ask for player name
cout << "Type your name (max 15 characters): ";
getline(cin, winner_name);
// If name is valid
if(isValidName(winner_name) && winner_name.length() <= 15){
string line;
vector<string> lb_entries;
int n_line = 0;
// Append to the end of the file
leaderboard << formated_time << " - " << winner_name << endl;
// Store all leaderboard entries in a vector
while(!leaderboard.eof()){
if(n_line >= 2){
getline(leaderboard, line);
lb_entries.push_back(line);
}
n_line++;
}
leaderboard.close();
//Everything works up until here, past here it doesn't do anything I want it to do
// Sort the leaderboard entries first by time, then by name
sort(lb_entries.begin(), lb_entries.end());
// Check if leaderboard has more than 10 entries to delete those past the limit
if(lb_entries.size() > 10){
// Truncates the vector from the 10th position forward
lb_entries.erase(lb_entries.begin()+9, lb_entries.end());
}
// Reopens the file in truncation mode to delete pre-existing leaderboard
leaderboard.open(filename, fstream::trunc);
// Format the file to have a table like shape
leaderboard << "| TIME - NAME |" << endl;
leaderboard << "------------------------------" << endl;
// Updates leaderboard
for(string entry : lb_entries){
leaderboard << entry << endl;
}
leaderboard.close();
break;
}
// If name not valid
else if(isValidName(winner_name) && winner_name.length() > 15){
cerr << endl << "Name has more than 15 characters! Please retry." << endl << endl;
}
else{
cerr << endl << "Not a valid name input!" << endl << endl;
}
}
// If file doesn't exist
else{
// Attempt to create the file
cout << "Creating leaderboard..." << endl;
ofstream leaderboard(filename);
// Check if file was created
if(!leaderboard){
cerr << "File could not be created" << endl;
}
else{
// Format the file to have a table like shape
leaderboard << "| TIME - NAME |" << endl;
leaderboard << "------------------------------" << endl;
leaderboard.close();
}
}
}
}
You need to break your problem down. What I would do is create a class that represents the LeaderBoards. It would actually consist of two classes. You could do one as an inner class of the others, but let's keep them separate:
class Leader {
public:
std::string time;
std::string name;
};
class LeaderBoard {
public:
std::vector<Leader> leaders;
void readFromFile(std::string fName);
void sort();
void writeToFile(std::string fName);
};
At that point, you need to implement three functions. None of them are very long.
void LeaderBoard::readFromFile(std::string fName) {
std::ifstream file(fName);
std::string line;
// skip the header
file.getline(line);
file.getline(line);
// Read the rest of the file.
while (file.getline(line)) {
// You'll need to parse the line into its parts
Leader leader(from the parts);
leaders.push_back(leader);
}
}
Yeah, I left some magic for you.
The write method would be very simple and just use an ofstream instead of an ifstream.
The sort method -- you can do a google for "c++ sort vector of objects" and get LOTS of examples.
In general, ALL programming can be broken down into smaller steps. If you're getting overwhelmed, break it down. This is one of the reasons you use an object-oriented language. If you don't know how to do something, create a class for it, then put methods in it for the smaller steps.
Then just figure out how to do small parts at a time. First: get data. Then print it out so you're sure you've got what you need.
If your code is more than about a screen or so, you're doing too much in one method. That's not an absolute, but at your level of coding, it's definitely true.
Small, tight methods. Small, tight methods are easier to write. Then string them together.
In this case:
Read the data
Sort the data
Write the data.
Each of these is easy to test individually.
Essentially, the objective is to read an input file (hence inFile and inFileName) and output a population growth with asterisks representing each 1000 people using an ID (ex. 1375892), going from the year 1900 to 2020 in 20-year increments.
So, 1 asterisk for 1000 people, 3 asterisks for 3000 people, etc. The input file has numbers like 5000 and 7000 that I need to use to calculate the number of asterisks I need (by dividing by 1000). Even with that, I'm trying to figure out the final step in converting asteriskNum (the number of asterisks I need to use) and have it output the string of asterisks, not an integer of how many asterisks I need.
I definitely know I'm missing SOMETHING, but even after asking my teacher and scouring through my textbook and notes, I can't figure out how to solve this specific issue.
#include<iostream>
#include<iomanip>
#include<string>
#include<fstream>
using namespace std;
int main(){
string asterisk = "*";
string firstName;
int PopNum{0};
int year{1900};
int asteriskNum{};
const string INTROLINE{"POPULATION GROWTH \n(each * represents 1000 people)"};
cout << INTROLINE << "\n";
string inFileName="DL8_L5_Morrison.txt";
ifstream inFile{inFileName};
if (inFile){
cout << inFileName << " opened for reading. \n";
inFile >> firstName;
while (not inFile.eof()){
inFile >> PopNum;
asteriskNum = PopNum/1000;
cout << year << " " << asteriskNum << " " << << "\n";
year+=20;
inFile.close();
}
else {
cout << inFileName << " did not open for reading. \n";}
cout<<"Goodbye!\n";
return EXIT_SUCCESS;
}
}
You can use a std::string object and use the constructor that takes a count and character as arguments (constructor version #2 here). This will work with an int for the count argument, but it is better to cast it to a size_t type (or just have the calculated value as a size_t in the first place):
//...
asteriskNum = PopNum/1000;
cout << year << " " << std::string(static_cast<size_t>(asteriskNum), '*') << std::endl;
//...
I have to write a program that allows the user to input a name from the keyboard. The program should then read from the file and search for matching name among the girls and boys. If a match is found it should output the rank of the name. The program should also indicate if there is no match.
Here is my program :
ifstream fin;
fin.open( "/Users/fashiontekk/Downloads/Assignment 3 Instructions/babyNames2017.dat" );
string nameInput;
string boyName;
string girlName;
int rank= 0;
int boyRank= 0;
int girlRank =0;
cout << " Which name would you like to check? < no space in names please > " << endl;
cin >> nameInput;
fin >> boyName;
fin >> girlName;
rank++;
cout << " After going through an extensive search here is what we found out - " << endl;
if (nameInput == boyName) {
cout << nameInput << " is ranked " << rank << " in popularity among boys. " << endl;
boyRank = rank;
}
if (nameInput == girlName) {
cout << nameInput << " is ranked " << rank << " in popularity among girls. " << endl;
girlRank = rank;
}
if (boyRank < 1 || boyRank > 1000) {
cout << nameInput << " is not ranked among the top 1000 boys name. " << endl;
}
if (girlRank < 1 || girlRank > 1000) {
cout << nameInput << " is not ranked among the top 1000 girls name. " << endl;
}
cout << " Hope that is the result you were looking for ... Ending program. " << endl;
fin.close();
return 0;
}
However, my output window says : Which name would you like to check? < no space in names please >
Program ended with exit code: 0Liam
After going through an extensive search here is what we found out -
Liam is ranked 1 in popularity among girls.
Liam is not ranked among the top 1000 boys name.
Hope that is the result you were looking for ... Ending program.
I tried to type in Liam which the most popular boys name according to the file provided. I feel like my coding is right however I can't spot the error.
It is my first year in Computer Science and I don't can't find my mistake.
OK, we've all been there at some point. You need to work on your debugging skills — you're gonna need them. In particular, spend some time learning to use gdb or whatever debugger you have available. A good debugger will let you step through a program a line at a time, watch variables, and generally checkout every possible thing that could be a problem.
So let's take a look at your code with an eye toward debugging it. It's handy that the message that's emitted comes right up near the top of the program — that really narrows down the places where you could be going wrong. Here's the first part of your program:
ifstream fin;
fin.open( "babyNames2017.dat" );
if (!fin) {
cout << " File not processed ";
return 0;
}
So, the first line just declares the variable for your input file. There's not much that can go wrong there. The next line opens the file... hmmm... I'm not sure if that might be a problem or not, so let's stick a pin in it for now and keep going. The next line, if (!fin) {, is a condition that only succeeds if !fin is true, which means that fin must evaluate to false to enter this block. And it clearly does enter this block, because that's the part of the code that emits the "File not processed" message. So fin must be 0, right? OK, so how can fin possibly be 0?
I don't have the C++ iostreams documentation handy, but you should go look up what that fin.open(...) call does if it fails. Given the way you've written the code, it looks very much like you'd expect failure to set fin to 0, right? So how can that call fail? Well, for starters, you're only supplying the file name... the working directory when you run the program might be set to something you don't expect, so the file isn't found. Or the file name might not match the name of the actual file. Remember that some file systems are case sensitive, and if you're working with such a file system then the open call will fail if the file is just named babynames2017.dat or BabyNames2017.dat or anything else that doesn't exactly match your file.
I am writing program for Library Management. I have a file Student.dat which have four columns. Initially when no book is issued it looks like this.
---------------Students List ----------------
Roll No. Name Book Issued Issued Book No.
001 Abhi 0 No
002 Ashu 0 No
After issuing book to '001'.
---------------Students List ----------------
Roll No. Name Book Issued Issued Book No.
001 Abhi 1 1001
02 Ashu 0 No
The roll number of second student becomes '02'.
This is complete issue function in library.cpp
void Library::book_issue()
{
//Some code
fp.open("Students.dat", std::ios::in | std::ios::out);
fp1.open("Books.dat", std::ios::in | std::ios::out);
//////////////////////////////////////////////////////////////////////
int oldPos = fp.tellg();
while (std::getline(fp, line) && !found_stu)
{
std::stringstream ss(line);
ss >> roll_n >> s_name >> tkn >> issued_b_num;
////////////
std::getline(ss, line);
if (boost::iequals(roll_n, r_num))
{
found_stu = true;
if (tkn == 0)
{
std::cout << "Enter Book No. : ";
std::getline(std::cin, b_num);
while (fp1 >> book_n >> b_name >> a_name && !found_book)
{
if (boost::iequals(book_n, b_num))
{
Book::show_book(book_n, b_name, a_name);
found_book = true;
tkn = 1;
Student::reset_issued_book_num();
issued_b_num = book_n;
//////////////////////////////////////////////////////////////////
fp.seekg(oldPos);
fp << roll_n << " " << s_name << " " << tkn << " " << issued_b_num << '\n';
std::cout << "Book Issued Successfully\n";
break;
}
}
if (!found_book)
{
std::cerr << "Book does not exist\n";
}
}
}
}
if (!found_stu)
{
std::cout << "Student record does not exist\n";
}
fp.close();
fp1.close();
}
I want to know whether I have used oldPos variable correctly?
Edit:
After assigning length of Issued Book No. as length of book number, I get repeated record.
---------------Students List ----------------
Roll No. Name Book Issued Issued Book No.
001 Abhi 1 1001
001 Abhi 1 1001
002 Ashu 0 No
The problem is that you overwrite the file that you read. So if one line would become longer, you'd overwrite characters of the next line(s).
As 002 becomes 02 and not 2, I'll assume that No in the file is followed by a whitespace. So if I use to show in a visible manner the LineFeed, the following content of your file:
...NO <LF>002...
will be overwriten with:
...1001<LF>02...
^ (end of the write, remaining chars unchanged)
So the 3 chars No are overwritten with 100, the LineFeed is overwritten with 1 and the 0 is overwritten with the new LineFeed.
If you want to write in-place like you try here, you must ensure that the size of each line remains fixed in all circumstances. So "No" should be followed by the number of space needed to match the length of a book number.
Other remarks
It's not the cause of the error, but tellg() returns a std::streampos, which can be much larger than an int. So I'd recommend to prefer:
auto oldPos = fp.tellg(); // here you're sure it's the right type
Note also that tellg()/seekg() are meant for input stream and tellp()/seekp()for output streams. Fortunately, for bidirectional file streams, there is only one position for reading and writing. But for other kind of bidirectional strings, this is not guaranteed (see this question).
Finally, if the goal of your repositionning is to overwrite the last line, read (and found) you should update it from time to time.