お知らせ

電子会議

ライブラリ

パレット

Delphi FAQ検索

Delphi FAQ一覧

サンプル蔵





FDelphi FAQ
16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル

"HTML Parser"



  みなさんこんにちは。6番会議室 #28014 からのツリーで話題の
HTML Parser を書いてみました。toCommentTag のあたりが、ちょっち
ゴリゴリ臭いですが(笑)



---------------------------------------------------------------------
unit HParse;

{$V+,B-,X+,T-,P+,H+,J+}

interface

{ THParser }

const
  toEof        = Char(0);
  toTag        = Char(25); { <xxxx }
  toOption     = Char(26); { xxxxxx }
  toParam      = Char(27); { ="xxxx" }
  toEndTag     = Char(28); { </xxxx> }
  toCommentTag = Char(29); { <!-- xx --> }
  toContext    = Char(30); { xxxx }

type
  THParser = class(TObject)
  protected
    FBuffer: PChar;
    FBufSize: Integer;
    FInTag: Boolean;
    FSourcePtr: PChar;
    FTokenPtr: PChar;
    FToken: Char;
    procedure SkipBlanks; virtual;
  public
    constructor Create(const S: String);
    destructor Destroy; override;
    function NextToken: Char; virtual;
    function SourcePos: Longint;
    function TokenString: String;
    property Token: Char read FToken;
  end;

implementation

{ THParser }

constructor THParser.Create(const S: String);
begin
  FBufSize := Length(S);
  GetMem(FBuffer, FBufSize + 1);
  if FBufSize > 0 then
    Move(S[1], FBuffer[0], FBufSize);
  FBuffer[FBufSize] := #0;
  FSourcePtr := FBuffer;
  FTokenPtr := FBuffer;
  NextToken;
end;

destructor THParser.Destroy;
begin
  if FBuffer <> nil then
    FreeMem(FBuffer, FBufSize + 1);
end;

procedure THParser.SkipBlanks;
begin
  while True do
  begin
    case FSourcePtr^ of
      #0:
        Exit;
      #33..#255:
        Exit;
    end;
    Inc(FSourcePtr);
  end;
end;

function THParser.SourcePos: Longint;
begin
  Result := FTokenPtr - FBuffer;
end;

function THParser.TokenString: string;
begin
  SetString(Result, FTokenPtr, FSourcePtr - FTokenPtr);
end;

function THParser.NextToken: Char;
var
  P: PChar;
begin
  SkipBlanks;
  P := FSourcePtr;
  FTokenPtr := P;
  if not FInTag then
    case P^ of
      '<':
        begin
          Inc(P);
          case P^ of
            '!':
              begin
                if Copy(String(P), 1, 3) = '!--' then
                begin
                  Result := toCommentTag;
                  Inc(P);
                  while True do
                  begin
                    if (P^ = #0) or
                       ((P^ = '-') and
                        (Copy(String(P), 1, 3) = '-->')) then
                    begin
                      while not (P^ in [#0, '>']) do Inc(P);
                      Break;
                    end;
                    Inc(P);
                  end;
                  if P^ = '>' then Inc(P);
                end
                else
                  Result := '<';
              end;
            '/':
              begin
                Result := toEndTag;
                while not (P^ in [#0, '>']) do Inc(P);
                if P^ = '>' then Inc(P);
              end;
            'A'..'Z', 'a'..'z':
              begin
                Result := toTag;
                FInTag := True;
                while P^ in ['A'..'Z', 'a'..'z'] do Inc(P);
              end;
          else
            Result := '<';
          end;
        end;
      #33..#59, #61..#255:
        begin
          Result := toContext;
          while not (P^ in [#0, '<']) do Inc(P);
        end;
    else
      Result := P^;
      if Result <> toEof then Inc(P);
    end
  else
  begin
    case P^ of
      'A'..'Z', 'a'..'z':
        begin
          Inc(P);
          Result := toOption;
          while P^ in ['A'..'Z', 'a'..'z'] do Inc(P);
        end;
      '=':
        begin
          Inc(P);
          Result := toParam;
          if P^ = '"' then
          begin
            Inc(P);
            while not (P^ in [#0, '"']) do Inc(P);
            if P^ = '"' then Inc(P);
          end
          else
            while P^ in [#33..#59, #61, #63..#126] do Inc(P);
        end;
    else
      Result := P^;
      if Result <> toEof then Inc(P);
    end;
    if Result in ['<', '>'] then FInTag := False;
  end;
  FSourcePtr := P;
  FToken := Result;
end;

end.
---------------------------------------------------------------------
使い方
Memo1 に適当な html 文を入れておき、Memo2 にトークンを取り出します。

uses
  HParse;
  
procedure TForm1.Button1Click(Sender: TObject);
var
  Parser: THParser;
  S: String;
begin
  Memo2.Lines.Clear;
  Memo2.Lines.BeginUpdate;
  try
    Parser := THParser.Create(Memo1.Lines.Text);
    try
      while Parser.Token <> toEof do
      begin
        case Parser.Token of
          toTag:        S := 'Tag     :';
          toOption:     S := 'Option  :';
          toParam:      S := 'Param   :';
          toEndTag:     S := 'EndTag  :';
          toCommentTag: S := 'Comment :';
          toContext:    S := 'Context :';
        else
          S := Format('%-8s:', [Parser.Token]);
        end;
        Memo2.Lines.Add(S + Parser.TokenString);
        Parser.NextToken;
      end;
    finally
      Parser.Free;
    end;
  finally
    Memo2.Lines.EndUpdate;
  end;
end;

このトークンを元に木を作るワケですが、木の仕様によっては、
トークンの切り出し方を変更する必要があるかもしれませんね。

ご意見がありましたら、6番会議室の方へお願いします。m(_ _)m

///// 本田勝彦 http://member.nifty.ne.jp/~katsuhiko/ /////

Original document by 本田勝彦        氏 ID:(VYR01647)


ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。

Copyright 1996-2002 Delphi Users' Forum