home *** CD-ROM | disk | FTP | other *** search
/ CD Shareware Magazine 1996 December / CD_shareware_12-96.iso / WIN / Programa / FPARSE20.ZIP / FPARSE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-08-13  |  14.7 KB  |  471 lines

  1. unit FParse;
  2. { Copyright 1996 Kevin L. Boylan }
  3.  
  4. {
  5.     TFParse will parse words (as defined by the developer or user) from a file, s string, or
  6.     a PChar.  For performance purposes, all parsing is done in memory.  If you are parsing from
  7.     a file and the file is > 64K, it will be buffered in 64K chunks.  If you are developing in
  8.   the Delphi 2 environment, the file will be buffered in 500,000 byte chunks.
  9.  
  10.     What is to be parsed is determined by setting one of the following three properties.
  11.     Setting one    of these properties will override any previous settings of one of the three:
  12.  
  13.     FileToParse:               The path and filename of a file that you wish to parsed.
  14.     StringToParse:                A String variable that you wish to parse.
  15.     PCharToParse:                A PChar variable that you wish to parse.
  16.  
  17.     What constitutes a word is defined through these properties:
  18.  
  19.     NormalCharacters:         The set of characters that can go into a word.
  20.     SignificantCharacters:    The set of characters that can go into a word only if surrounded
  21.                                     by NormalCharacters.
  22.     InsignificantCharacters:The set of characters that will be ignored and removed from between
  23.                                     two NormalCharacters.
  24.     MIN_WORD_LENGTH:            The minimum length of words that will be accepted.
  25.     MAX_WORD_LENGTH:            The maximum length of words returned.  Longer words are truncated.
  26.     CommonWords:                This list of words will be ingored, not returned. If you don't want
  27.                                     to make use of this property, you can set the property CmnWrdsActive
  28.                                     to False.
  29.  
  30.     Once these properties are set, you then repeatedly call GetNext, which returns a string
  31.     containing the next word from the file, string, or PChar.  When GetNext returns a null
  32.     string ('') then all words have been parsed.
  33.  
  34.     5/12/96            Released version 1.0
  35.     5/15/96            Modified CreateCharSets, changing for loops to while loops because
  36.                         the counter was being modified inside the loops with inc(i,2).  This
  37.                         problem kept the component from installing under Delphi 2.0  This
  38.                         modification resulted in version 1.1.
  39.     6/3/96            Fixed bugs which kept strings and PChars from being parsed.  Also fixed
  40.                         a problem where the PercentDone property could be greater then 100% by
  41.                  adding the MinLongInt function.  version 1.2
  42.     7/22/96            Added HTML parsing capabilites (minus special character handling) and condensed 
  43.                         CreateCharSets member function by consolidating code into an internal procedure;
  44.   7/23/96            Added special character (i.e. ")handling capabilites to HTML parsing.
  45.   8/12/96            Added OnHTMLTag event
  46.     8/13/96            Added AutoDetect capability
  47. }
  48.  
  49. interface
  50.  
  51. uses
  52.     SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  53.     Forms, Dialogs;
  54.  
  55. type
  56.   EDocType = (dtAscii, dtHTML);
  57.   TCharSet = Set of Char;
  58.   EOFileException = class(Exception);
  59.   THTMLTagEvent = procedure(HTMLTag: String) of object;
  60.  
  61.     TFParse = class(TComponent)
  62.   private
  63.       { Private declarations }
  64.       FFileToParse: String;
  65.       theFile: TFileStream;
  66.       ByteCount: LongInt;
  67.       TotalSize: LongInt;
  68.       FBuffer,CurrChar,EOBuffer: PChar;
  69.       BuffLen: LongInt;
  70.       NormChar,SigChar,InSigChar: TCharSet;
  71.       FNormalCharacters: String;
  72.       FSignificantCharacters: String;
  73.       FInsignificantChars: String;
  74.       FCommonWords: TStrings;
  75.       FCmnWrdsActive: Boolean;
  76.       FMIN_WORD_LENGTH: Integer;
  77.       FMAX_WORD_LENGTH: Integer;
  78.     FDocumentType: EDocType;
  79.     FOnHTMLTag: THTMLTagEvent;
  80.     FAutoDetect: Boolean;
  81.     protected
  82.       { Protected declarations }
  83.       procedure SetFileToParse( FName: String );
  84.       procedure SetStringToParse( theStr: String );
  85.       procedure SetPCharToParse( thePChar: PChar );
  86.       function  GetPercentDone: LongInt;
  87.       function  NextChar: Boolean;
  88.       procedure Loaded; override;
  89.       procedure CreateCharSets;
  90.       procedure SetCommonWords(Value: TStrings);
  91.       procedure Init;
  92.     function    DetermineType: EDocType;
  93.     public
  94.       { Public declarations }
  95.       constructor Create( AOwner: TComponent ); override;
  96.       destructor Destroy; override;
  97.  
  98.       property StringToParse: String write SetStringToParse;
  99.       property PCharToParse: PChar write SetPCharToParse;
  100.       function GetNext: String;
  101.       property PercentDone: LongInt read GetPercentDone;
  102.     published
  103.       { Published properties }
  104.       property FileToParse: String read FFileToParse write SetFileToParse;
  105.       property MIN_WORD_LENGTH: Integer read FMIN_WORD_LENGTH write FMIN_WORD_LENGTH default 1;
  106.       property MAX_WORD_LENGTH: Integer read FMAX_WORD_LENGTH write FMAX_WORD_LENGTH default 20;
  107.       property NormalCharacters: String read FNormalCharacters write FNormalCharacters;
  108.       property SignificantCharacters: String read FSignificantCharacters
  109.                                                                                write FSignificantCharacters;
  110.       property InsignificantChars: String read FInsignificantChars write FInsignificantChars;
  111.       property CommonWords: TStrings read FCommonWords write SetCommonWords;
  112.       property CmnWrdsActive: Boolean read FCmnWrdsActive write FCmnWrdsActive;
  113.     property DocumentType: EDocType read FDocumentType write FDocumentType default dtAscii;
  114.     property AutoDetect: Boolean read FAutoDetect write FAutoDetect default False;
  115.     { Published events }
  116.     property OnHTMLTag: THTMLTagEvent read FOnHTMLTag write FOnHTMLTag;
  117.     end;
  118.  
  119.  
  120. procedure Register;
  121. function MinLongInt( Long1, Long2: LongInt ): LongInt;
  122.  
  123. implementation
  124.  
  125. const
  126.  {$IFDEF WIN32}
  127.       MAX_WORD = 500000;
  128.  {$ELSE}
  129.     MAX_WORD = 65526;
  130.  {$ENDIF}
  131.  
  132. constructor TFParse.Create( AOwner: TComponent );
  133. begin
  134.     Inherited Create( AOwner );
  135.     MIN_WORD_LENGTH := 1;                    { Default minimum word length }
  136.     MAX_WORD_LENGTH := 20;                  { Default maximum word length }
  137.     FCmnWrdsActive := False;                 { By default, CommonWords is not active }
  138.   FAutoDetect := False;               { By default, AutoDetect is false }
  139.   FDocumentType := dtAscii;                { Plain text file is default }
  140.     If (csDesigning In ComponentState) then
  141.      begin
  142.         NormalCharacters := '0-9A-Za-z'; { Default chars that make words }
  143.         SignificantCharacters := './';   { Default chars that can be inside words only }
  144.         InsignificantChars := ',';       { Default chars that will be stripped from words }
  145.      end;
  146.      FCommonWords := TStringList.Create;
  147. end;
  148.  
  149. destructor TFParse.Destroy;
  150. begin
  151.     theFile.Free;
  152.     StrDispose( FBuffer );
  153.     FCommonWords.Free;
  154.     Inherited Destroy;
  155. end;
  156.  
  157. procedure TFParse.Loaded;
  158. begin
  159.     Inherited Loaded;
  160.     if not (csDesigning In ComponentState) then
  161.         CreateCharSets;  { Create the Sets of Chars }
  162. end;
  163.  
  164. procedure TFParse.SetCommonWords(Value: TStrings);
  165. begin
  166.     FCommonWords.Assign(Value);
  167. end;
  168.  
  169.  
  170. procedure TFParse.CreateCharSets;
  171. { Converts from the string representation of the character sets to real
  172.     sets of char }
  173.  procedure CreateSet( var CharSet: TCharSet; SetDef: String );
  174.  var
  175.     i: Integer;
  176.     j: Char;
  177.  begin
  178.   CharSet := [];
  179.   i := 1;
  180.     While i <= Length(SetDef) do  { Read each character }
  181.      begin
  182.         If (i < Length(SetDef)-1) and (SetDef[i+1] = '-') then
  183.          begin      { Handle ranges }
  184.             For j := SetDef[i] to SetDef[i+2] do
  185.                 CharSet := CharSet + [j];    { Add each in range to set }
  186.             Inc(i,2);
  187.          end
  188.         else
  189.          begin
  190.             CharSet := CharSet + [SetDef[i]];    { Add to set }
  191.          end;
  192.         Inc(i,1);
  193.      end;
  194.  end;  { procedure CreateSet }
  195.  
  196. begin
  197.     If NormalCharacters = '' then
  198.         NormalCharacters := '0-9A-Za-z';                      { If none set, then use the default }
  199.     CreateSet( NormChar, NormalCharacters );                {Create Normal Character Set }
  200.   CreateSet( SigChar, SignificantCharacters );         {Create Significant Character Set }
  201.   CreateSet( InSigChar, InsignificantChars );            {Create InSignificant Character Set }
  202. end;
  203.  
  204. procedure TFParse.Init;
  205. { Initializes variables prior to any parsing }
  206. begin
  207.     CurrChar := FBuffer-1; { Will start out incrementing CurrChar }
  208.     ByteCount := 1;
  209.     EOBuffer := FBuffer + BuffLen;
  210.     EOBuffer^ := #0;
  211. end;
  212.  
  213. function TFParse.DetermineType: EDocType;
  214. { Determines what type of document (i.e. Ascii or HTML) has been loaded in
  215.     the buffer. }
  216. const
  217.     DetectStringSize = 50;
  218. var
  219.     DetectStr: String[DetectStringSize];
  220.   i: PChar;
  221. begin
  222.     Result := dtAscii;        { assume ascii }
  223.     i := FBuffer;
  224.   DetectStr := '';
  225.   While ((i<EOBuffer) and (i<FBuffer+DetectStringSize)) do
  226.    begin
  227.         DetectStr := DetectStr + i^;
  228.      Inc(i);
  229.    end;
  230.   DetectStr := LowerCase(DetectStr);
  231.   { --- Check for HTML }
  232.   If (ExtractFileExt( FFileToParse ) = '.htm' )
  233.       or ((Pos('<', DetectStr) > 0) and (Pos('html', DetectStr) > 0)) then
  234.             Result := dtHTML;
  235. end;
  236.  
  237. procedure TFParse.SetFileToParse( FName: String );
  238. { Opens the file to be parsed.  Maximum block size to be read in at one time is
  239.     MAX_WORD which should be close to 64K for 16 bit and much greater for 32 bit }
  240. begin
  241.     theFile.Free;
  242.     try
  243.         theFile := TFileStream.Create( FName, fmOpenRead );
  244.         If (theFile.Size > MAX_WORD) then
  245.             BuffLen := MAX_WORD
  246.         else
  247.             BuffLen := theFile.Size+1;
  248.         TotalSize := theFile.Size;
  249.         StrDispose(FBuffer);
  250.         FBuffer := StrAlloc(BuffLen);
  251.         Init;
  252.         theFile.Read( FBuffer^, BuffLen-1 );
  253.      If AutoDetect then
  254.          FDocumentType := DetermineType;
  255.     except
  256.         On EFOpenError do
  257.             raise EFOpenError.CreateFmt( 'File %s could not be found', [FName] );
  258.     end;  { except }
  259. end;
  260.  
  261. procedure TFParse.SetStringToParse( theStr: String );
  262. { Sets the buffer to be parsed from a String instead of from a file }
  263. begin
  264.     FFileToParse := '';
  265.     TotalSize := Length(theStr);
  266.     BuffLen := TotalSize + 1;
  267.     StrDispose(FBuffer);
  268.     FBuffer := StrAlloc( BuffLen );
  269.     StrPCopy( FBuffer, theStr );
  270.     Init;
  271.   If AutoDetect then
  272.         FDocumentType := DetermineType;
  273. end;
  274.  
  275. procedure TFParse.SetPCharToParse( thePChar: PChar );
  276. { Sets the buffer to be parsed from a PChar instead of from a file }
  277. begin
  278.     FFileToParse := '';
  279.     TotalSize := StrLen( thePChar );
  280.     BuffLen := TotalSize + 1;
  281.     StrDispose(FBuffer);
  282.     FBuffer := StrAlloc( BuffLen );
  283.     StrCopy( FBuffer, thePChar );
  284.     Init;
  285.   If AutoDetect then
  286.         FDocumentType := DetermineType;
  287. end;
  288.  
  289. function TFParse.GetNext: String;
  290. { returns the next word from the file }
  291. var
  292.   tmpWord: String[255];
  293.   tmpChar: PChar;
  294.  
  295.   procedure GetHTMLLiteral;
  296.   { It is assumed that tmpChar is pointing at a '&' }
  297.   const
  298.       LiteralStringSize = 3;
  299.   var
  300.       AStr: String[LiteralStringSize];
  301.   begin
  302.       AStr := '';
  303.       tmpChar^ := #0;
  304.      Inc(tmpChar);
  305.      If tmpChar^ = '#' then { its an ascii representation }
  306.       begin
  307.           tmpChar^ := #0;
  308.         Inc(tmpChar);
  309.           While (tmpChar^ <> ';') and (Length(AStr) <= LiteralStringSize) do
  310.          begin
  311.            AStr := AStr + tmpChar^;
  312.            tmpChar^ := #0;
  313.            Inc(tmpChar);
  314.          end;
  315.         tmpChar^ := Chr( StrToInt(AStr) );
  316.       end
  317.      Else                   { its an entity code-word }
  318.       begin
  319.           AStr := tmpChar^;
  320.         tmpChar^ := #0;
  321.         Inc(tmpChar);
  322.         While (tmpChar^ <> ';') do
  323.          begin
  324.            AStr := AStr + tmpChar^;
  325.            tmpChar^ := #0;
  326.            Inc(tmpChar);
  327.          end;
  328.         AStr := LowerCase(AStr);
  329.         If AStr = 'amp' then
  330.             tmpChar^ := '&'
  331.         Else If AStr = 'gt' then
  332.             tmpChar^ := '>'
  333.         Else If AStr = 'lt' then
  334.             tmpChar^ := '<'
  335.         Else If AStr = 'quot' then
  336.             tmpChar^ := '"'
  337.         Else
  338.             tmpChar^ := #0;    { we don't handle any others at this time }
  339.       end;
  340.   end;
  341.  
  342.   procedure ClearHTML;
  343.   { Zero's out irrelevant HTML code so that parsing will ignore }
  344.   var
  345.       HTMLTag: String;
  346.   begin
  347.       tmpChar := CurrChar;
  348.      HTMLTag := '';
  349.      Repeat
  350.        If tmpChar^ = '<' then
  351.        begin
  352.            While True do
  353.          begin
  354.                If (tmpChar^ = '>') then                 { End on the '>' character }
  355.             begin
  356.                 if Assigned(FOnHTMLTag) then     { Handle the OnHTMLTag event }
  357.                begin
  358.                    HTMLTag := HTMLTag + tmpChar^;
  359.                  FOnHTMLTag( HTMLTag );
  360.                  HTMLTag := '';
  361.                end;
  362.                 tmpChar^ := #0;                   { Zero out so will be ignored }
  363.               Inc(tmpChar);
  364.                  Break;
  365.             end;
  366.            If Assigned(FOnHTMLTag) then         { Collect tag contents if OnHTMLTag is set }
  367.                HTMLTag := HTMLTag + tmpChar^;
  368.            tmpChar^ := #0;
  369.              Inc(tmpChar);
  370.          end;
  371.          end
  372.       Else
  373.          GetHTMLLiteral;                         {It's a literal, not a Tag }
  374.      Until (tmpChar^ <> '<') and (tmpChar^ <> '&')
  375.   end;
  376.  
  377. begin
  378.  try  { EOFileException is raised when end of buffer is reached }
  379.   tmpChar := nil;
  380.     Repeat
  381.         Repeat
  382.             Result := '';
  383.         tmpWord := '';
  384.             While (NextChar) do  { Find next NormChar to start a word }
  385.          begin  { First check to see if it's HTML stuff to skip }
  386.              { Begin HTML Check ***** }
  387.              If ((DocumentType = dtHTML) and (tmpChar < CurrChar)) and ((CurrChar^ = '<') or (CurrChar^ = '&')) then
  388.                If Length(tmpWord) > 0 then
  389.                begin
  390.                    Dec(CurrChar);
  391.                  Dec(ByteCount);
  392.                   Break;
  393.                end
  394.               Else
  395.                    ClearHTML;
  396.            { End HTML Check ***** }
  397.  
  398.            If (CurrChar^ = #0) or (CurrChar ^ in InSigChar) then
  399.                Continue;
  400.                 If (CurrChar^ in NormChar) or
  401.             ((CurrChar^ in SigChar) and ((CurrChar+1)^ in NormChar)) then
  402.             begin
  403.                 If Length(tmpWord) < 255 then
  404.                         tmpWord := tmpWord + CurrChar^;
  405.             end
  406.            Else
  407.                Break;
  408.          end;
  409.         Until Length( tmpWord ) >= MIN_WORD_LENGTH;
  410.  
  411.         Result := Copy(LowerCase(tmpWord),1,MAX_WORD_LENGTH);
  412.     Until ( (not CmnWrdsActive) or (FCommonWords.IndexOf( Result ) = -1) )
  413.  
  414.  except
  415.      on EOFileException do   { End of buffer has been reached }
  416.    begin
  417.        StrDispose( FBuffer );
  418.         FBuffer := nil;
  419.         exit;
  420.    end;
  421.  end;
  422.  
  423. end;
  424.  
  425. function TFParse.NextChar: Boolean;
  426. { points at the next character in the buffer and reacts according to what's there }
  427. var
  428.     AmountToRead,
  429.     AmountRead:        LongInt;
  430. begin
  431.     Result := True;
  432.     Inc(CurrChar);
  433.     Inc(ByteCount);
  434.     If CurrChar < EOBuffer then
  435.         exit;
  436.     If ByteCount > TotalSize+1 then
  437.           raise EOFileException.Create('End of File');
  438.     CurrChar := FBuffer;
  439.     AmountToRead := BuffLen - 1;
  440.      FBuffer[0] := #0;
  441.     AmountRead := theFile.Read( (FBuffer+StrLen(FBuffer))^, AmountToRead );
  442.     If AmountRead < AmountToRead then
  443.      begin
  444.         EOBuffer := CurrChar + AmountRead;
  445.         EOBuffer^ := #0;
  446.      end;
  447. end;
  448.  
  449. function TFParse.GetPercentDone: LongInt;
  450. { returns the percentage of the file that has been parsed }
  451. begin
  452.     Result := MinLongInt(((ByteCount * 100) div TotalSize), 100 );
  453. end;
  454.  
  455. function MinLongInt( Long1, Long2: LongInt ): LongInt;
  456. { returns the smallest of two long integers }
  457. begin
  458.     If Long1 < Long2 then
  459.         result := Long1
  460.     Else
  461.         result := Long2;
  462. end;
  463.  
  464. procedure Register;
  465. { Registers the component }
  466. begin
  467.     RegisterComponents('Samples', [TFParse]);
  468. end;
  469.  
  470. end.
  471.