#include <fstream.h>
#include <iomanip.h>
#include <string.h>
#include <time.h>
/*-------------------------------------------------------------------------
LineFile : a class to manage a file of lines of fixed length.
version 1 : 17-05-02, University of Crete, Paris Pamfilos
version 2 : 21-05-02 append even if user forgot 'a'
TODO
security ??? (io-errors ??) (line-length ??)
count characters on entry and replace line with two if too long?
change max line-length of catalog
order catalog
interchange two lines
insert line at
number lines
phone catalog specialize
folder catalog specialize
add abstraction level user interface, hidding details of io
use multimap with key the name and value the rest of string
---------------------------------------------------------------------------*/
class LineFile
{
private:
enum
{
MXCH = 1024, // max line width
MAIN_LINE_WIDTH = 74, // actually used line width
MARGIN = 5,
TOTAL_WIDTH = MAIN_LINE_WIDTH + MARGIN + 1
};
char fName[100]; // name of catalog
char line[MXCH]; // line buffer
char myTime[20]; // store time stamp
int wordLineStart; // line to start search
bool wordFound;
string wordSearch; // word to search for
public:
LineFile();
LineFile( char* fname ) ;
~LineFile();
int AcceptDelete();
void AppendLine();
void AppendLineEndFile();
void DeleteLine();
void DisplayNrCommentLine( int n, char* comment );
int FindLineWord( string & word , int lineToStart);
int GetLineLength() ;
int GetAnInteger();
void GetThisLine( int count );
void GetWordSearchFor( string & str );
void InitLineFile();
char MenuSelection();
void NewWordSearch();
int NumOfLines();
void OldWordSearch();
void OpenSeekLine( fstream & kata, int count, bool toRead=true );
void OverideFileWithBuffer( char * pch, unsigned int size );
void PrintLines( );
void PutInBuffer( char* pch, unsigned int offset, unsigned int size );
void ReplaceLine( );
void ShowLine( );
void ShowLineFound( int lineFound );
void TimeString();
int WordFoundInLine( string & word );
void WriteLine( fstream & kata );
void WriteMessage( char* mess );
};
/*-------------------------------------------------------------------------
construct , destruct
---------------------------------------------------------------------------*/
LineFile::LineFile( char* fname ) { strcpy(fName,fname); InitLineFile(); }
LineFile::LineFile(){ InitLineFile(); }
LineFile::~LineFile(){}
/*-------------------------------------------------------------------------
Ask user for deletion and check
---------------------------------------------------------------------------*/
int LineFile::AcceptDelete()
{
int n = GetAnInteger(); // get line-nr from user
GetThisLine(n);
DisplayNrCommentLine(n, "-line delete ?? (y = yes )" );
cin.getline(line,MXCH); // get line from cin
if( line[0] != 'y' ) n = 0;
return n;
}
/*-------------------------------------------------------------------------
Ask user for a line and append to end of file
---------------------------------------------------------------------------*/
void LineFile::AppendLine( )
{
WriteMessage( "write a line") ; // WARNING check length ?????
cin.getline(line,MXCH); // get line from cin
AppendLineEndFile();
}
/*-------------------------------------------------------------------------
store content of <line> as last line
---------------------------------------------------------------------------*/
void LineFile::AppendLineEndFile()
{
fstream kata(fName, ios::app); // open to append
WriteLine( kata );
kata.close();
}
/*-------------------------------------------------------------------------
delete line from file, use <ios::binary> to save white space
The line splits the file in two parts of (n-1) lines before and
(N-n) lines after the deleted line.
---------------------------------------------------------------------------*/
void LineFile::DeleteLine( )
{
unsigned int n = (unsigned int)AcceptDelete(); // ask user etc...
if(n == 0 ) return ; // user canceled ... do nothing
unsigned int N = NumOfLines(); // find total lines
unsigned int totpos = (N-1)*TOTAL_WIDTH; // size after deletion
unsigned int beforeSize = (n-1)*TOTAL_WIDTH; // size before line
unsigned int afterSize = totpos-beforeSize; // size after line
char * pch = new char[ totpos ]; // to receive the new text
if(n>1) // if not first line delete
PutInBuffer( pch, 0, beforeSize ); // store upper part to buffer
if(n != N) // i.e. if not last line to delete
PutInBuffer( pch+beforeSize, n*TOTAL_WIDTH, afterSize );
// store lower part to buffer
OverideFileWithBuffer( pch, totpos ); // write back buffer to file
delete[] pch; // WARNING memory ???
}
/*-------------------------------------------------------------------------
display n-th number with a comment and the <line>
---------------------------------------------------------------------------*/
void LineFile::DisplayNrCommentLine( int n, char* comment )
{
WriteMessage("-----------");
cout << n << comment << endl <<line << endl;
WriteMessage("-----------");
}
/*-------------------------------------------------------------------------
Find first occurence of <word> in lines , starting with <lineToStart>
line. Returns the line-nr containing or -1 if not found.
---------------------------------------------------------------------------*/
int LineFile::FindLineWord( string & word , int lineToStart)
{
fstream kata;
OpenSeekLine( kata, lineToStart ); // open and go to start of <lineToStart>
int n(lineToStart);
int result;
while( kata.getline( line, MXCH ) ) // read line from file
{
result = (int)WordFoundInLine( word ) ; // test if word there
if( result > -1 ){ kata.close(); return n;}
n++;
}
kata.close();
return -1;
}
/*-------------------------------------------------------------------------
---------------------------------------------------------------------------*/
int LineFile::GetLineLength() { return strlen(line);}
/*-------------------------------------------------------------------------
Get an integer from user, adjust it to be inside the range of existing
lines of the file
---------------------------------------------------------------------------*/
int LineFile::GetAnInteger()
{
int N = NumOfLines();
cout << "Give an integer 0 < n <= " << N << endl;
cin.getline(line,MXCH);
int n = atoi(line); // transform to int
if( (n<=N) && ( n > 0) ) return n; // normal case
else if( n <=0 ) return 1; // if <0 replace by 1
else return N; // if > N replace by N
}
/*-------------------------------------------------------------------------
Store into <line> the count-line of the file
---------------------------------------------------------------------------*/
void LineFile::GetThisLine( int count )
{
fstream kata;
OpenSeekLine(kata, count);
kata.getline( line, MXCH );
kata.close();
}
/*-------------------------------------------------------------------------
Ask user to give a word to search for. Use whole line to avoid
interfer with redundant words (after the search word)
uses the one-time initalized char* <seps> (separators of words)
---------------------------------------------------------------------------*/
void LineFile::GetWordSearchFor( string & str )
{
static char seps[] = " ,\t\n";
WriteMessage(" Give a word to search for:");
cin.getline( line, MXCH ); // get a line from user
if(strlen(line)==0) str = "****";
else
str = strtok( line, seps ); // its first word (token)
}
/*-------------------------------------------------------------------------
used to initialize the class and print useful info on file status
---------------------------------------------------------------------------*/
void LineFile::InitLineFile()
{
wordFound = false;
WriteMessage("-----------");
cout << "line width = " << TOTAL_WIDTH << endl;
cout << NumOfLines() << " lines in file: " << fName << endl;
WriteMessage("-----------");
}
/*-------------------------------------------------------------------------
Write the menu and get user's selection
---------------------------------------------------------------------------*/
char LineFile::MenuSelection()
{
WriteMessage("<MENU>\n AddLine (a) , DeleteLine (d) , PrintLines (p) , ReplaceLine (r)");
WriteMessage(" ShowLine (s) , WordSearch (f) , AgainSearch (g) , Quit (q)");
WriteMessage("</MENU>");
cin.getline(line,MXCH); // eat all the line but use only 1st char
if( GetLineLength() >10 ) // append !! *** user forgot to press a before !!
{
AppendLineEndFile();
}
return line[0];
}
/*-------------------------------------------------------------------------
Start new search for a word stored in <wordSearch>
---------------------------------------------------------------------------*/
void LineFile::NewWordSearch()
{
wordFound=false; // not found yet
wordLineStart=1; // line to start search
GetWordSearchFor( wordSearch ); // get from user
if( wordSearch.length() != 0 ) // is a real word ??
{
int lineFound = FindLineWord( wordSearch , wordLineStart);
if( lineFound >-1 ) // if some line found
{
wordFound = true; // prepare for next search
ShowLineFound( lineFound );
}
else //nothing found
{
cout << "passed end of file" << endl; // inform user that no other line ...
}
}
}
/*-------------------------------------------------------------------------
Open the file and calc its number of lines WARNING
---------------------------------------------------------------------------*/
int LineFile::NumOfLines()
{
fstream kata(fName, ios::binary | ios_base::in );
kata.seekg(0,ios_base::end); // set ptr to end of file
int n ( kata.tellg()/TOTAL_WIDTH); // this is the total line-nr
kata.close();
return n;
}
/*-------------------------------------------------------------------------
Continue searching in other lines for the word: <wordSearch>
This function continues the work started by NewWordSearch()
---------------------------------------------------------------------------*/
void LineFile::OldWordSearch()
{
if( wordFound ) // something has been found previously
{
int lineFound;
if( !(wordLineStart<NumOfLines()) ||
(lineFound = FindLineWord( wordSearch , wordLineStart+1)) == -1 )
{
cout << "passed end of file" << endl; // nothing new found
wordLineStart = 1; // to restart search
}
else
{
ShowLineFound( lineFound );
}
}
else
NewWordSearch(); // start a new search
}
/*-------------------------------------------------------------------------
open file and seekg (to read) seekp (to write) at the beginning of count-line
---------------------------------------------------------------------------*/
void LineFile::OpenSeekLine( fstream & kata, int count, bool toRead /*true*/ )
{
fstream::pos_type pos((count-1)*TOTAL_WIDTH);
kata.open(fName, ios_base::in | ios_base::out ); // WARNING errors ??
if(toRead) kata.seekg(pos);
else kata.seekp(pos);
}
/*-------------------------------------------------------------------------
---------------------------------------------------------------------------*/
void LineFile::OverideFileWithBuffer( char * pch, unsigned int size )
{
ofstream kata(fName, ios::binary | ios_base::trunc );
kata.write( pch, size );
kata.close();
}
/*-------------------------------------------------------------------------
Print on screen all lines of the file
---------------------------------------------------------------------------*/
void LineFile::PrintLines()
{
int n(0);
WriteMessage("-----------");
ifstream kata; // open file to read from
kata.open(fName);
while( kata.getline( line, MXCH ) )
{
cout << line << endl;
n++;
}
WriteMessage("-----------");
cout << n << " lines in " << fName << endl;
}
/*-------------------------------------------------------------------------
Put in buffer <size> bytes from file, start reading in file from <offset>
---------------------------------------------------------------------------*/
void LineFile::PutInBuffer( char* pch, unsigned int offset, unsigned int size )
{
fstream kata(fName, ios::binary | ios_base::out | ios_base::in );
kata.seekg(offset); // start here reading from file
kata.read( pch, size ); // ... put <size> bytes in pch
kata.close();
}
/*-------------------------------------------------------------------------
Replace a line of file with a new one
---------------------------------------------------------------------------*/
void LineFile::ReplaceLine( )
{
int n = AcceptDelete(); // ask user etc...
if(n == 0) return; // user canceled
WriteMessage("Write new line");
cin.getline(line,MXCH); // get line from user
fstream kata;
OpenSeekLine(kata, n, false ); // false = to write
WriteLine( kata );
kata.close();
}
/*-------------------------------------------------------------------------
Show line by its line-number
---------------------------------------------------------------------------*/
void LineFile::ShowLine( )
{
int n = GetAnInteger();
GetThisLine(n);
DisplayNrCommentLine( n , "-line is:" );
}
/*-------------------------------------------------------------------------
Show line found on search request
---------------------------------------------------------------------------*/
void LineFile::ShowLineFound( int lineFound )
{
wordLineStart = lineFound; // update line-count for next search
GetThisLine( lineFound );
string comment("-line contains word < ");
comment += wordSearch;
comment += " > ";
DisplayNrCommentLine( lineFound, const_cast<char*>( comment.data() ) );
}
/*-------------------------------------------------------------------------
Get actual time and trasforme to a date-stamp in <myTime> string
---------------------------------------------------------------------------*/
void LineFile::TimeString()
{
time_t long_time; // get time is seconds
time( &long_time );
struct tm *newtime; // to format time-info
newtime = localtime( &long_time ); // transform to date
sprintf(myTime, "%2d/%2d/%2d ", newtime->tm_mday ,
newtime->tm_mon+1,
newtime->tm_year-100 );
}
/*-------------------------------------------------------------------------
returns the offset or -1 in not found
---------------------------------------------------------------------------*/
int LineFile::WordFoundInLine( string & word )
{
string L(line);
return L.find( word );
}
/*-------------------------------------------------------------------------
Write content of <line>, fill with <space> + set time stamp + <new-line>
to complete a new line in file.
---------------------------------------------------------------------------*/
void LineFile::WriteLine( fstream & kata )
{
kata << line << setw(MAIN_LINE_WIDTH-GetLineLength()) << myTime
<< setw(MARGIN) << "**\n";
}
/*-------------------------------------------------------------------------
---------------------------------------------------------------------------*/
void LineFile::WriteMessage( char* mess )
{
cout << mess << endl;
}
/*-------------------------------------------------------------------------
---------------------------------------------------------------------------*/
int main(int argc, char* argv[])
{
LineFile lf("kat.txt");
lf.TimeString();
char ch(1);
while( ch != 'q' )
{
ch = lf.MenuSelection();
switch(ch)
{
case 'a':
lf.AppendLine();
break;
case 'd':
lf.DeleteLine();
break;
case 'p':
lf.PrintLines();
break;
case 'r':
lf.ReplaceLine();
break;
case 's':
lf.ShowLine();
break;
case 'f':
lf.NewWordSearch();
break;
case 'g':
lf.OldWordSearch();
break;
case 'q': //exit
break;
default:
cout << "Error, please type only one of: a, d, p, r, s, f, g and q = quit" << endl;
break;
}
}
}
Σημείωσε ότι γιά την μετατροπή του κώδικα σε κείμενο HTML χρησιμοποίησα το ελεύθερα διατιθέμενο πρόγραμμα μετατροπής Lore's source converter.