C++/C++ program
Expert: Ralph McArdell - 5/2/2004
QuestionI would like this program to output the file in reverse order i.e the last five starting with the latest. Can you help please?
#include<iostream>
#include<fstream>
#include<stdlib>
#include<time>
#include<string>
#include<ctype>
#define KBYTE 1024
using namespace std;
class bookobject
{
protected: //new fields here which means
char name[KBYTE]; //changeing the edit function
int number; //and books2file -
//so new constructor << =
private: //and methods to access and change fields
//keep getName() setName() getNumber() setNumber()
public:
bookobject(void)
{
strcpy(name, "");
number = -1;
}
bookobject(string nam, int num)
{
strcpy(name, nam.c_str());
number = num;
}
bookobject(bookobject& bo)
{
strcpy(name, bo.name);
number = bo.number;
}
string getName(void)
{
return string(name);
}
void setName(string s)
{
strcpy(name, s.c_str());
}
int getNumber(void)
{
return number;
}
void setNumber(int n)
{
number = n;
}
int numCompare(bookobject& bo)
{
return number - bo.number;
}
int alphaCompare(bookobject& bo)
{
return strcmp(name, bo.name);
}
const bookobject& operator = (const bookobject& bo)
{
if (this != &bo)
{
strcpy(name, bo.name);
number = bo.number;
}
return *this;
}
friend ostream& operator<< (ostream& os, bookobject& bo)
{
os << bo.name << ", " << bo.number << endl;
return os;
}
};
class bookwithposition : public bookobject
{
private:
int position;
public:
bookwithposition(void) : bookobject()
{
position = -1;
}
bookwithposition(bookobject& bo) : bookobject(bo)
{
position = -1;
}
bookwithposition(bookwithposition& rowp) :bookobject(rowp)
{
position = rowp.position;
}
void setPosition(int n)
{
position = n;
}
int getPosition(void)
{
return position;
}
const bookwithposition& operator =
(const bookwithposition& rowp)
{
if (this != &rowp)
{
strcpy(name, rowp.name);
number = rowp.number;
position = rowp.position;
}
return *this;
}
friend ostream& operator<<
(ostream& os, bookwithposition& rowp)
{
os << rowp.name << ", " << rowp.number << ": ";
os << rowp.position << endl;
return os;
}
};
template <class Buck>
class BuckDoubleLinkedList
{
class DoubleNode //inner class for nodes
{
private:
Buck obj; //data object
DoubleNode *next; //pointers for links
DoubleNode *previous;
public:
DoubleNode(void)
{
next = NULL; //pointing nowhere
previous = NULL;
}
DoubleNode(Buck& o)
{
obj = o; //with a data object
next = NULL;
previous = NULL;
}
~DoubleNode(void)
{
}
DoubleNode *getNext()
{
return next;
}
DoubleNode *getPrevious()
{
return previous;
}
void setNext(DoubleNode *hp)
{
next = hp;
}
void setPrevious(DoubleNode *hp)
{
previous = hp;
}
Buck& getObject()
{
return obj;
}
};
private:
DoubleNode head, tail; //dummy nodes for head and tail
DoubleNode *current, *temp; //pointers for insertion and deletion
char alphanumeric; //indicates alphabetic or numeric order
public:
BuckDoubleLinkedList(char n)
{
head.setNext(&tail); //link head to tail
tail.setPrevious(&head); //link tail to head
if (n == 'n' || n == 'N')
{
alphanumeric = 'n'; //numeric order
}
else if (n == 'a' || n == 'A')
{
alphanumeric = 'a'; //alphabetic order
}
}
~BuckDoubleLinkedList(void)
{
DoubleNode *p = head.getNext();
while (p != &tail)
{
p = p->getNext(); //delete all the nodes
if (alphanumeric == 'n')
{ //by number
deleteNumber(p->getPrevious()->getObject().getNumber());
}
else
{ //or by name
deleteName(p->getPrevious()->getObject().getName());
}
}
}
void insert(Buck& o)
{
DoubleNode *t = new DoubleNode(o); //new node with data in it
DoubleNode *n = head.getNext(), *p;
while (n != &tail) //while not at end of list
{ //if this is the righ place to insert
if ((alphanumeric == 'n' && n->getObject().numCompare(o)>0) ||
(alphanumeric == 'a' && n->getObject().alphaCompare(o)>0))
{
break; //do not go any further
}
n = n->getNext(); //try next position
}
p = n->getPrevious(); //before insertion point
t->setNext(n); //set links for new node to list neighbours
t->setPrevious(p);
p->setNext(t); //set links for neighbours to new node
n->setPrevious(t);
}
bool search(int k) //search for number in sequential search
{
DoubleNode *p = head.getNext();
while (p != &tail)
{ //data object must have getNumber() implemented
if (p->getObject().getNumber() == k)
{
return true;
}
p = p->getNext();
}
return false;
}
bool search(string str) //search for string in sequential serrch
{
DoubleNode *p = head.getNext();
while (p != &tail)
{ //data object must have getName() implemented
if (strcmp(p->getObject().getName().c_str(), str.c_str())==0)
{
return true;
}
p = p->getNext();
}
return false;
}
DoubleNode *position(int k) //gets physical position in memory
{
DoubleNode *p = head.getNext();
while (p != &tail)
{
if (p->getObject().getNumber() == k) //using data object's getNumber()
return p;
p = p->getNext();
}
return NULL;
}
DoubleNode *position(string str) //gets physical position in memory
{
DoubleNode *p = head.getNext();
while (p != &tail)
{ //using data object's getNumber()
if (strcmp(p->getObject().getName().c_str(), str.c_str())==0)
return p;
p = p->getNext();
}
return NULL;
}
void deleteNumber(int k)
{
DoubleNode *p;
if ((p = position(k)) != NULL) //find actual position
{
current = p->getPrevious(); //the one before
temp = p->getNext(); //the one after
current->setNext(temp); //reset links
temp->setPrevious(current);
delete p; //release node
}
}
void deleteName(string str)
{
DoubleNode *p;
if ((p = position(str)) != NULL) //if name exists
{
current = p->getPrevious(); //get pos before
temp = p->getNext(); //get pos after
current->setNext(temp); //set new links
temp->setPrevious(current);
delete p; //release old node
}
}
void printItem(int num)
{
DoubleNode *p;
if ((p = position(num)) != NULL) //if number exists
{
cout << p->getObject(); //display it
}
else
{
cout << num << " is not in list" << endl;
}
}
void printItem(string str)
{
DoubleNode *p;
if ((p = position(str)) != NULL) //if name exists
{
cout << p->getObject(); //display it
}
else
{
cout << str << " is not in list" << endl;
}
}
void printList(void)
{
DoubleNode *p = head.getNext(); //start with first elemennt
while (p != &tail) //while not at end
{
cout << p->getObject(); //get element and output it
p = p->getNext(); //go to next
}
cout << endl;
}
};
int filesize(string fname);
bool another(string str);
void books2file(string fname);
void file2list(string fname, BuckDoubleLinkedList<bookwithposition>& lst);
void edit(string fname);
const char *backupname(string fname);
void initialiseToFalse(bool *pos, int siz);
void backupFile(string fname);
void deleteFromBackup(string fname, bool *toDel);
void erase(string fname);
void latest(string fname);
void main(void)
{
string filename;
cout << "Enter filename: " << flush;
cin >> filename;
books2file(filename);
//edit(filename);
erase(filename);
system("pause");
}
void latest(string fname)
{
}
void erase(string fname)
{
BuckDoubleLinkedList<bookwithposition> list('a');
file2list(fname, list);
list.printList(); //display list of contents
int pos, size = filesize(fname)/sizeof(bookobject);
bool *toDelete = new bool[size];
initialiseToFalse(toDelete, size);
do
{
cout << "Enter position to delete: " << flush;
cin >> pos;
if (pos >= 0 && pos < size)
toDelete[pos] = true;
} while (another("position to delete"));
backupFile(fname);
deleteFromBackup(fname, toDelete);
BuckDoubleLinkedList<bookwithposition> newlist('a');
file2list(fname, newlist); //make new list to test
newlist.printList(); //display list of contents to test
}
void deleteFromBackup(string fname, bool *toDel)
{
ofstream os(fname.c_str(), ios::binary); //empty new file
ifstream is(backupname(fname), ios::binary); //backup file to copy
int pos = 0;
bookobject bo;
while(is.read(reinterpret_cast<char *>(&bo), sizeof(bookobject)))
{
if (!toDel[pos++])
{ //if not to be deleted, copy to new file
os.write(reinterpret_cast<char *>(&bo), sizeof(bookobject));
}
}
os.close(); // close files when done
is.close();
}
void initialiseToFalse(bool *pos, int siz)
{
for (int i = 0; i < siz; i++)
{
pos[i] = false;
}
}
void backupFile(string fname)
{
bookobject bo;
ifstream is(fname.c_str(), ios::binary); //open our file to make backup
ofstream os(backupname(fname), ios::binary); //create new backup file
while (is.read(reinterpret_cast<char *>(&bo), sizeof(bookobject)))
{
//copy from file to backup file
os.write(reinterpret_cast<char *>(&bo), sizeof(bookobject));
}
is.close(); //close both files when we have done
os.close();
}
const char *backupname(string fname)
{
return (fname + ".bak").c_str();
}
void edit(string fname)
{
BuckDoubleLinkedList<bookwithposition> list('a');
file2list(fname, list);
list.printList(); //display list of contents
//open binary input/output file
fstream fs(fname.c_str(), ios::in | ios::out | ios::binary);
int size = filesize(fname)/sizeof(bookobject);
do
{
int pos, newnum;
string newstr;
bookobject bo;
cout << "Input position to edit: " << flush;
cin >> pos; //get position
if (pos >= 0 && pos < size) //if in range
{
fs.seekg(pos * sizeof(bookobject)); //go to position
fs.read(reinterpret_cast<char *>(&bo), sizeof(bookobject)); //read number
cout << "change name " << bo.getName() << " to: " << flush;
cin >> newstr; //get new name
bo.setName(newstr);
cout << "change number " << bo.getNumber() << " to: " << flush;
cin >> newnum; //get new number
bo.setNumber(newnum);
fs.seekp(pos * sizeof(bookobject)); //move to correct pos
fs.write(reinterpret_cast<char *>(&bo), sizeof(bookobject)); //overwrite
}
} while (another("book to edit"));
fs.close(); //close when done
BuckDoubleLinkedList<bookwithposition> newlist('a'); //new list
file2list(fname, newlist); //make new list to test
newlist.printList(); //display list of contents to test
}
void books2file(string fname)
{
//open binary output file in APPEND MODE
ofstream os(fname.c_str(), ios::app | ios::binary);
do
{
string nam;
int num;
cout << "Input name of book: " << flush;
cin >> nam; //get name
cout << "Input number of book: " << flush;
cin >> num; //get number
bookobject bo(nam, num); //make instance of bookobject
os.write(reinterpret_cast<char *>(&bo), sizeof(bookobject));
} while (another("book to add"));
os.close(); //close when done
}
void file2list(string fname, BuckDoubleLinkedList<bookwithposition>& lst)
{
int index = 0;
bookobject bo;
ifstream is(fname.c_str(), ios::binary); //open binary input file
//while we can read a book from the file
while (is.read(reinterpret_cast<char *>(&bo), sizeof(bookobject)))
{
bookwithposition bwp(bo); //make book with position
bwp.setPosition(index++); //set position
lst.insert(bwp); //add to list
}
is.close(); //close file when we have done3
cout <<endl;
}
int filesize(string fname)
{
ifstream is(fname.c_str(), ios::binary);
is.seekg(0, ios::end);
int pos = is.tellg();
is.close();
return pos;
}
bool another(string str)
{
char yesno = '\0';
do
{
cout << "Another " << str << " Y/N?" << flush;
cin >> yesno;
yesno = (char)toupper(yesno);
}
while (yesno != 'Y' && yesno != 'N');
return yesno == 'Y';
}
AnswerI probably could give you a better answer if you could reduce the amount of code posted - I do not have the time to spend reverse engineering your code - and a quick look raises questions along the lines of "eh? Which file? Which function?" I think it may be books2file from the name, but I am not sure as it seems to just write the book information you enter directly to a file - in which case what is the doubly linked list for?
In short this is a case of not being able to see the wood for the trees - you have given me too much detail in the code. However you have not given many hints in the question text that accompanies your code or better yet an overview of what your code should be doing.
I would suggest that you enter book data objects onto a list then output the book items in the list to the file in the required order. For example you could obtain the book item data and push them to the top of the list each time. Then you have to just output each item on the list from top to bottom (or front to back if you prefer) and the last entry will be the first written to the file and the first item the last written to the file. Alternatively you can append the items to the end of the list each time and then output them last to first (bottom to top or back to front) - which you can do as you have a doubly linked list
Why did you create your own list? Why not use std::list? std::list is part of C++ and so others like myself are familiar with it - also why recreate the wheel? Or possibly you could use std::vector or std::deque if you only needed to insert or remove items to the end or the beginning or the end. If you do not have information on the standard library containers or other STL parts check out the documentation at
http://www.sgi.com/tech/stl/ If you require an implementation of the C++ standard library then check out
http://www.stlport.org/
Note that using the standard library components allows you to use other standard library functionality - such as sorting your container's data - for very little effort.
On another note try to avoid the use of #defines as in:
#define KBYTE 1024
You can do something like the following instead:
const int KBYTE = 1024;
or using constructor call syntax:
const int KBYTE(1024);
or even:
int const KBYTE(1024);
Alternatively you could place the constant in the scope of the class namespace where it is used to size the name array:
class bookobject
{
static const int KBYTE = 1024;
// ...
};
Note that you cannot use constructor style syntax here as the compiler tends to think you are trying to declare a member function!
class bookobject
{
// OOPS! - compiler thinks KBYTE is a static member function...
static const int KBYTE(1024);
// ...
};
or if your compiler does not support initialising static constant members in class definitions you can use the enum trick instead:
class bookobject
{
enum { KBYTE = 1024 };
// ...
};
You can adjust the access as required and if it is public access KBYTE from elsewhere like so: bookobject::KBYTE
A better choice of name would be something like MAX_NAME_LENGTH - it states exactly what the value is used for.
Finally, beware of using:
using namespace std;
and the like in header files - it can cause unexpected problems see
http://www.gotw.ca/gotw/053.htm answer to question 2 (about a third of the way down the article). There are other GoTW articles - see the index page at
http://www.gotw.ca/gotw/index.htm