利用Java編寫HTML檔案分析程式(轉)

BSDLite發表於2007-08-17
利用Java編寫HTML檔案分析程式(轉)[@more@]摘要:本文從實踐的角度重點闡述Java語言中輸入流類StreamTokenizer在編寫HTML檔案分析程式中的應用,並介紹了以位元組為單位下載Web頁面的例程。

一、概述

Web伺服器的核心是對HTML檔案中的各標記(Tag)作出正確的分析,一種程式語言的解釋程式也是對原始檔中的保留字進行分析再做解釋的。實際應用中,我們也常常會遇到需要對某一特定型別檔案進行關鍵字分析的情況,比如,需要將某個HTML檔案下載並同時下載與之相關的.gif、.class等檔案,此時就要求對HTML檔案中的標記進行分離,找出所需的檔名及目錄。在Java出現以前,類似工作需要對檔案中的每個字元進行分析,從中找出所需部分,不僅程式設計量大,且易出錯。筆者在近期的專案中利用Java的輸入流類StreamTokenizer進行HTML檔案的分析,效果較好。在此,我們要實現從已知的Web頁面下載HTML檔案,對其進行分析後,下載該頁面中包含的HTML檔案(如果在Frame中)、影像檔案和Class(Java Applet)檔案。

二、StreamTokenizer類

StreamTokenizer即令牌化輸入流的作用是將一個輸入流中變成令牌流。令牌流中的令牌實體有三類:單詞(即多字元令牌)、單字元令牌和空白(包括Java和C/C++中的說明語句)。

StreamTokenizer類的構造器為: StreamTokenizer(InputStream in)

該類有一些公有例項變數:ttype、sval和nval ,分別表示令牌型別、當前字串值和當前數字值。當我們需要取得令牌(即HTML中的標記)之間的字元時,應訪問變數sval。而讀向下一個令牌的方法是呼叫nextToken()。方法nextToken()的返回值是int型,共有四種可能的返回:

StreamTokenizer.TT_NUMBER: 表示讀到的令牌是數字,數字的值是double型,可以從例項變數nval中讀取。

StreamTokenizer.TT_WORD: 表示讀到的令牌是非數字的單詞(其他字元也在其中),單詞可以從例項變數sval中讀取。

StreamTokenizer.TT_EOL: 表示讀到的令牌是行結束符。

如果已讀到流的盡頭,則nextToken()返回TT_EOF。

開始呼叫nextToken()之前,要設定輸入流的語法表,以便使分析器辨識不同的字元。WhitespaceChars(int low, int hi)方法定義沒有意義的字元的範圍。WordChars(int low, int hi)方法定義構造單詞的字元範圍。

三、程式實現

1、HtmlTokenizer類的實現

對某個令牌流進行分析之前,首先應對該令牌流的語法表進行設定,在本例中,即是讓程式分出哪個單詞是HTML的標記。下面給出針對我們需要的HTML標記的令牌流類定義,它是StreamTokenizer的子類:


import java.io.*;
import java.lang.String;
class HtmlTokenizer extends
StreamTokenizer {
//定義各標記,這裡的標記僅是本例中必須的,
可根據需要自行擴充
static int HTML_TEXT=-1;
static int HTML_UNKNOWN=-2;
static int HTML_EOF=-3;
static int HTML_IMAGE=-4;
static int HTML_FRAME=-5;
static int HTML_BACKGROUND=-6;
static int HTML_APPLET=-7;

boolean outsideTag=true; //判斷是否在標記之中

//構造器,定義該令牌流的語法表。
public HtmlTokenizer(BufferedReader r) {
super(r);
this.resetSyntax(); //重置語法表
this.wordChars(0,255); //令牌範圍為全部字元
this.ordinaryChar('< '); //HTML標記兩邊的分割符
this.ordinaryChar('>');
} //end of constructor

public int nextHtml(){
int token; //令牌
try{
switch(token=this.nextToken()){
case StreamTokenizer.TT_EOF:
//如果已讀到流的盡頭,則返回TT_EOF
return HTML_EOF;
case '< ': //進入標記欄位
outsideTag=false;
return nextHtml();
case '>': //出標記欄位
outsideTag=true;
return nextHtml();
case StreamTokenizer.TT_WORD:
//若當前令牌為單詞,判斷是哪個標記
if (allWhite(sval))
return nextHtml(); //過濾其中空格
else if(sval.toUpperCase().indexOf("FRAME")
!=-1 && !outsideTag) //標記FRAME
return HTML_FRAME;
else if(sval.toUpperCase().indexOf("IMG")
!=-1 && !outsideTag) //標記IMG
return HTML_IMAGE;
else if(sval.toUpperCase().indexOf("BACKGROUND")
!=-1 && !outsideTag) //標記BACKGROUND
return HTML_BACKGROUND;
else if(sval.toUpperCase().indexOf("APPLET")
!=-1 && !outsideTag) //標記APPLET
return HTML_APPLET;
default:
System.out.println ("Unknown tag: "+token);
return HTML_UNKNOWN;
} //end of case
}catch(IOException e){
System.out.println("Error:"+e.getMessage());}
return HTML_UNKNOWN;
} //end of nextHtml

protected boolean allWhite(String s){//過濾所有空格
//實現略
}// end of allWhite

} //end of class

以上方法由筆者在近期專案中測試透過,作業系統為Windows NT4,程式設計工具使用Inprise Jbuilder3。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617542/viewspace-963801/,如需轉載,請註明出處,否則將追究法律責任。

相關文章