home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1998 May / Pcwk5b98.iso / Borland / Cplus45 / BC45 / XREF.PAK / XREF.CPP < prev    next >
C/C++ Source or Header  |  1995-08-29  |  15KB  |  408 lines

  1. /*------------------------------------------------------------------------*/
  2. /*                                                                        */
  3. /*  XREF.CPP                                                              */
  4. /*                                                                        */
  5. /*  Copyright (c) 1993 Borland International                              */
  6. /*  All Rights Reserved.                                                  */
  7. /*                                                                        */
  8. /*  Text cross referencing example                                        */
  9. /*                                                                        */
  10. /*------------------------------------------------------------------------*/
  11.  
  12. /*------------------------------------------------------------------------*/
  13. /*                                                                        */
  14. /*  Containers Used: TBinarySearchTreeImp, TIBinarySearchTreeImp,         */
  15. /*  TSVectorImp.                                                          */
  16. /*  Other Classes: string.                                                */
  17. /*                                                                        */
  18. /*  Data files provided: trivial.dat and text.dat.                        */
  19. /*------------------------------------------------------------------------*/
  20.  
  21. #include <classlib\binimp.h>
  22. #include <classlib\vectimp.h>
  23. #include <cstring.h>
  24. #include <regexp.h>
  25. #include <fstream.h>
  26. #include <strstrea.h>
  27. #include <iomanip.h>
  28. #include <dir.h>
  29.  
  30. // MaxRefs is the maximum number of references that a word can have 
  31. // before it is rejected (not put into the cross reference table).
  32. //
  33. const unsigned MaxRefs = 6;
  34.  
  35. // Indicates the minimum length a word must have to be a candidate to be
  36. // put into the cross reference table.
  37. //
  38. const unsigned MinWordLength = 3;
  39.  
  40. /*------------------------------------------------------------------------*/
  41. /*                                                                        */
  42. /*  class TRecord                                                         */
  43. /*                                                                        */
  44. /*      Keeps track of the lines that a word appears on.                  */
  45. /*                                                                        */
  46. /*  Public Interface:                                                     */
  47. /*                                                                        */
  48. /*      TRecord( string word, unsigned lineNum );                         */
  49. /*                                                                        */
  50. /*          Creates record with given word and line number.               */
  51. /*                                                                        */
  52. /*      int operator == ( const TRecord& record ) const;                  */
  53. /*                                                                        */
  54. /*          Returns 1 if the Words compare equal, 0 otherwise.            */
  55. /*                                                                        */
  56. /*      int operator < ( const TRecord& record ) const;                   */
  57. /*                                                                        */
  58. /*          Returns 1 if the Word in this record is lexically before the  */
  59. /*          Word in the record parameter.                                 */
  60. /*                                                                        */
  61. /*      int AddLineRef( unsigned lineNum );                               */
  62. /*                                                                        */
  63. /*          Add given page number to word's list of line references.  If  */
  64. /*          the number of references >= 'MaxRefs' then return 0 and       */
  65. /*          do not enter given line number in line ref list, else         */
  66. /*          return 1                                                      */
  67. /*                                                                        */
  68. /*      string GetWord() const;                                           */
  69. /*                                                                        */
  70. /*          Return copy of 'Word' member.                                 */
  71. /*                                                                        */
  72. /*  Global functions:                                                     */
  73. /*                                                                        */
  74. /*      ostream& operator << ( ostream&, const TRecord& );                */
  75. /*                                                                        */
  76. /*          Writes the record to the ostream.                             */
  77. /*                                                                        */
  78. /*------------------------------------------------------------------------*/
  79.  
  80. class TRecord
  81. {
  82.  
  83. public:
  84.  
  85.     TRecord( const string& word, unsigned lineNum );
  86.  
  87.     int operator == ( const TRecord& record ) const;
  88.     int operator < ( const TRecord& record ) const;
  89.     int AddLineRef( unsigned lineNum );
  90.     string GetWord() const;
  91.     unsigned GetPageCount() const;
  92.     friend ostream& operator << ( ostream&, const TRecord& );
  93.  
  94. private:
  95.  
  96.     string Word;
  97.     TSVectorImp<unsigned> LineRef;
  98.  
  99. };
  100.  
  101. TRecord::TRecord( const string& word, unsigned lineNum ) :
  102.     Word( word ),
  103.     LineRef(MaxRefs)
  104. {
  105.     AddLineRef(lineNum);
  106. }
  107.  
  108. inline int TRecord::operator == ( const TRecord& record ) const
  109. {
  110.     return Word == record.Word;
  111. }
  112.  
  113. inline int TRecord::operator < ( const TRecord& record ) const
  114. {
  115.     return Word < record.Word;
  116. }
  117.  
  118. int TRecord::AddLineRef( unsigned lineNum )
  119. {
  120.     if( LineRef.Count() < MaxRefs )
  121.         {
  122.         if( LineRef.Find( lineNum ) == UINT_MAX )
  123.             LineRef.Add( lineNum );
  124.         return 1;
  125.         }
  126.     return 0;
  127. }
  128.  
  129. string TRecord::GetWord() const
  130. {
  131.     return Word;
  132. }
  133.  
  134. unsigned TRecord::GetPageCount() const
  135. {
  136.     return LineRef.Count();
  137. }
  138.  
  139. ostream& operator << ( ostream& os, const TRecord& rec )
  140. {
  141.     char buf[80];
  142.     ostrstream temp( buf, sizeof(buf) );
  143.     temp.setf( ios::left );
  144.  
  145.     temp << setw(20) << rec.Word;
  146.  
  147.     for( unsigned i = 0; i < rec.LineRef.Count()-1; i++ )
  148.         {
  149.         temp << rec.LineRef[i] << ',';  // write all but last line number
  150.         }                               // followed by a comma
  151.     temp << rec.LineRef[i] << ends;     // write last line number
  152.  
  153.     os << buf;
  154.     return os;
  155. }
  156.  
  157. /*------------------------------------------------------------------------*/
  158. /*                                                                        */
  159. /*  class TCrossRef                                                       */
  160. /*                                                                        */
  161. /*      Responsible for reading a trivial word file and text file,        */
  162. /*      then creating and printing a cross reference list of the words    */
  163. /*      in the text file.  The trivial word file contains words that will */
  164. /*      not be included in the cross reference.  The text file consists   */
  165. /*      of ordinary ASCII text. The ouput is an alphabetical list of      */
  166. /*      words in the text file and the numbers of the lines on which they */
  167. /*      occur.                                                            */
  168. /*                                                                        */
  169. /*  Public Interface:                                                     */
  170. /*                                                                        */
  171. /*      TCrossRef( string trivialWordFileName, string textFileName );     */
  172. /*                                                                        */
  173. /*          Read trivial word file and store in a binary search tree.     */
  174. /*          Then read each word in from the text file and process it.     */
  175. /*          If either file cannot be opened or an error occurs during     */
  176. /*          reading a TXFileReadError exception is thrown.                */
  177. /*                                                                        */
  178. /*      ~TCrossRef();                                                     */
  179. /*                                                                        */
  180. /*          Flushes the list of words in the cross reference.             */
  181. /*                                                                        */
  182. /*      PrintCrossReferences( ostream& );                                 */
  183. /*                                                                        */
  184. /*          Print each word and the lines it appears on in the text file. */
  185. /*                                                                        */
  186. /*      Nested class TXFileReadError.                                     */
  187. /*                                                                        */
  188. /*          An object of type TXFileReadError is thrown if either the     */
  189. /*          trivial word or cross reference file could not be opened or   */
  190. /*          read. To find out which file it was use the WhichFile()       */
  191. /*          member function.                                              */
  192. /*                                                                        */
  193. /*------------------------------------------------------------------------*/
  194.  
  195. class TCrossRef
  196. {
  197.  
  198. public:
  199.  
  200.     class TXFileReadError : public xmsg
  201.     {
  202.     public:
  203.         TXFileReadError( const string& file )
  204.             : xmsg( string( "Error reading file: " ) + file ), File(file) {}
  205.  
  206.         string WhichFile() const;
  207.  
  208.     private:
  209.         string File;
  210.     };
  211.  
  212.     TCrossRef( string trivialWordFileName, string textFileName );
  213.     ~TCrossRef();
  214.  
  215.     void PrintCrossReferences( ostream& );
  216.  
  217. private:
  218.  
  219.     void ReadTrivialWords( string trivialWordFileName );
  220.     void ReadWords( string bookWordFileName );
  221.     void ProcessWord( string word, unsigned page );
  222.  
  223.     static void PrintRecord( TRecord _BIDSFAR &o, void _BIDSFAR*arg );
  224.     void Reject( string word, string reason );
  225.  
  226.     TBinarySearchTreeImp<string> RejectedWords;
  227.     TIBinarySearchTreeImp<TRecord> IndexOfWords;
  228.     unsigned CurrentPage;
  229.  
  230. };
  231.  
  232. //
  233. //  Construct a TCrossRef object, reading words from the file
  234. //  specified by trivialWordFileName into the tree of rejected
  235. //  words, then reading the text from the file specified by
  236. //  testFileName and building the cross reference tree.
  237. //
  238. TCrossRef::TCrossRef( string trivialWordFileName, string textFileName ) :
  239.     CurrentPage(1)
  240. {
  241.     ReadTrivialWords( trivialWordFileName );
  242.     ReadWords( textFileName );
  243. }
  244.  
  245. //
  246. //  Clean up.
  247. //
  248. TCrossRef::~TCrossRef()
  249. {
  250.     IndexOfWords.Flush(1);
  251. }
  252.  
  253. //
  254. //  Read trivial words from the specified file, convert them
  255. //  to lower case, and store them in the rejected words tree.
  256. //
  257. void TCrossRef::ReadTrivialWords( string trivialWordFileName )
  258. {
  259.     ifstream TrivialWordFile( trivialWordFileName.c_str() );
  260.     if( !TrivialWordFile )
  261.         throw TXFileReadError( trivialWordFileName );
  262.  
  263.     string wordFromFile;
  264.  
  265.     while( TrivialWordFile >> wordFromFile )
  266.         {
  267.         wordFromFile.to_lower();
  268.         RejectedWords.Add( wordFromFile );
  269.         }
  270. }
  271.  
  272. //
  273. //  Read words from the specified file and insert them into
  274. //  the cross reference tree. Along the way, convert to lower
  275. //  case and strip out any non-text characters and all text 
  276. //  following any such characters within a word. (Quick and
  277. //  dirty hack for dealing with possessives, etc.)
  278. //
  279. void TCrossRef::ReadWords( string textFileName )
  280. {
  281.     ifstream XrefFile( textFileName.c_str() );
  282.  
  283.     if( !XrefFile )
  284.         throw TXFileReadError( textFileName );
  285.  
  286.     TRegexp WordOnly( "[a-z]+" );
  287.     unsigned currentLine = 1;
  288.     while( XrefFile )
  289.         {
  290.         char buf[128];
  291.         XrefFile.getline( buf, sizeof(buf) );
  292.         if( XrefFile )
  293.             {
  294.             string word;
  295.             istrstream in( buf );
  296.             while( in )
  297.                 {
  298.                 in >> word;
  299.                 if( in )
  300.                     {
  301.                     word.to_lower();    // store as lower case
  302.  
  303.                     // use WordOnly as filter to strip out trailing
  304.                     // non-text characters and any characters following.
  305.                     ProcessWord( word(WordOnly), currentLine );
  306.                     }
  307.                 }
  308.             currentLine++;
  309.             }
  310.         }
  311. }
  312.  
  313. //
  314. //  Figure out whether a word should be added to the cross
  315. //  reference tree. If not, explain. If so, add it.
  316. //
  317. void TCrossRef::ProcessWord( string word, unsigned lineNum )
  318. {
  319.  
  320.     if( RejectedWords.Find( word ) != 0 )
  321.         Reject( word, "found in rejected word file" );
  322.     else if( word.length() < MinWordLength )
  323.         Reject( word, "too short" );
  324.     else
  325.         {
  326.         TRecord *recordToUpdate = IndexOfWords.Find( &TRecord(word,lineNum) );
  327.         if( recordToUpdate == 0 )
  328.             {
  329.             //  The word isn't in the tree yet. Add a
  330.             //  new record to contain it.
  331.             IndexOfWords.Add( new TRecord(word,lineNum) );
  332.             }
  333.         else if( recordToUpdate->GetPageCount() >= MaxRefs )
  334.             {
  335.             Reject( word, "reference count too large" );
  336.             IndexOfWords.Detach( recordToUpdate, 1 );
  337.             }
  338.         else
  339.             {
  340.             //  The word is already in the tree. Add the
  341.             //  additional line reference.
  342.             recordToUpdate->AddLineRef( lineNum );
  343.             }
  344.         }
  345. }
  346.  
  347. //
  348. //  Callback for ForEach() to print a record to the stream
  349. //  pointed to by 'arg'.
  350. //
  351. void TCrossRef::PrintRecord( TRecord &o, void *arg )
  352. {
  353.     (*(ostream*)arg) << o << endl;
  354. }
  355.  
  356. //
  357. //  Print the cross references to the specified stream.
  358. //
  359. void TCrossRef::PrintCrossReferences( ostream& os )
  360. {
  361.     os << "References:\n\n";
  362.     os.setf( ios::left, ios::adjustfield );
  363.     IndexOfWords.ForEach( PrintRecord, &os );
  364. }
  365.  
  366. //
  367. //  Process rejected words by adding the word to the
  368. //  rejected words list and printing a message indicating
  369. //  why the word was rejected.
  370. //
  371. void TCrossRef::Reject( string word, string reason )
  372. {
  373.     RejectedWords.Add( word );
  374.     cerr << '\'' << word << "' rejected, " << reason << endl;
  375. }
  376.  
  377. string TCrossRef::TXFileReadError::WhichFile() const
  378. {
  379.     return File;
  380. }
  381.  
  382. /*------------------------------------------------------------------------*/
  383.  
  384. int main()
  385. {
  386.     try
  387.         {
  388.         string TrivialWordFile;
  389.         string XrefFile;
  390.  
  391.         cerr << "Enter name of trivial word file: ";
  392.         cin >> TrivialWordFile;
  393.  
  394.         cerr << "Enter name of file to cross reference: ";
  395.         cin >> XrefFile;
  396.  
  397.         TCrossRef CrossRef( TrivialWordFile, XrefFile );
  398.         CrossRef.PrintCrossReferences( cout );
  399.  
  400.         }
  401.     catch( const TCrossRef::TXFileReadError& xReadError )
  402.         {
  403.         cout << xReadError.why() << endl;
  404.         return 1;
  405.         }
  406.     return 0;
  407. }
  408.