一、概述
Web服務(wù)器的核心是對HTML文件中的各標(biāo)記(Tag)作出正確的分析,一種編程語言的解釋程序也是對源文件中的保留字進(jìn)行分析再做解釋的。實(shí)際應(yīng)用中,我們也常常會遇到需要對某一特定類型文件進(jìn)行關(guān)鍵字分析的情況,比如,需要將某個(gè)HTML文件下載并同時(shí)下載與之相關(guān)的.gif、.class等文件,此時(shí)就要求對HTML文件中的標(biāo)記進(jìn)行分離,找出所需的文件名及目錄。在Java出現(xiàn)以前,類似工作需要對文件中的每個(gè)字符進(jìn)行分析,從中找出所需部分,不僅編程量大,且易出錯(cuò)。筆者在近期的項(xiàng)目中利用Java的輸入流類StreamTokenizer進(jìn)行HTML文件的分析,效果較好。在此,我們要實(shí)現(xiàn)從已知的Web頁面下載HTML文件,對其進(jìn)行分析后,下載該頁面中包含的HTML文件(如果在Frame中)、圖像文件和Class(Java Applet)文件。
二、StreamTokenizer類
StreamTokenizer即令牌化輸入流的作用是將一個(gè)輸入流中變成令牌流。令牌流中的令牌實(shí)體有三類:單詞(即多字符令牌)、單字符令牌和空白(包括Java和C/C++中的說明語句)。
StreamTokenizer類的構(gòu)造器為: StreamTokenizer(InputStream in)
該類有一些公有實(shí)例變量:ttype、sval和nval ,分別表示令牌類型、當(dāng)前字符串值和當(dāng)前數(shù)字值。當(dāng)我們需要取得令牌(即HTML中的標(biāo)記)之間的字符時(shí),應(yīng)訪問變量sval。而讀向下一個(gè)令牌的方法是調(diào)用nextToken()。方法nextToken()的返回值是int型,共有四種可能的返回:
StreamTokenizer.TT_NUMBER: 表示讀到的令牌是數(shù)字,數(shù)字的值是double型,可以從實(shí)例變量nval中讀取。
StreamTokenizer.TT_WORD: 表示讀到的令牌是非數(shù)字的單詞(其他字符也在其中),單詞可以從實(shí)例變量sval中讀取。
StreamTokenizer.TT_EOL: 表示讀到的令牌是行結(jié)束符。
如果已讀到流的盡頭,則nextToken()返回TT_EOF。
開始調(diào)用nextToken()之前,要設(shè)置輸入流的語法表,以便使分析器辨識不同的字符。WhitespaceChars(int low, int hi)方法定義沒有意義的字符的范圍。WordChars(int low, int hi)方法定義構(gòu)造單詞的字符范圍。
三、程序?qū)崿F(xiàn)
1、HtmlTokenizer類的實(shí)現(xiàn)
對某個(gè)令牌流進(jìn)行分析之前,首先應(yīng)對該令牌流的語法表進(jìn)行設(shè)置,在本例中,即是讓程序分出哪個(gè)單詞是HTML的標(biāo)記。下面給出針對我們需要的HTML標(biāo)記的令牌流類定義,它是StreamTokenizer的子類:
?1
???????? import?java.io.*;?
?2
import?java.lang.String;?
?3
class?HtmlTokenizer?extends?
?4
StreamTokenizer?
{?
?5
//定義各標(biāo)記,這里的標(biāo)記僅是本例中必須的,?
?6
可根據(jù)需要自行擴(kuò)充?
?7
?static?int?HTML_TEXT=-1;?
?8
?static?int?HTML_UNKNOWN=-2;?
?9
?static?int?HTML_EOF=-3;?
10
?static?int?HTML_IMAGE=-4;?
11
?static?int?HTML_FRAME=-5;?
12
?static?int?HTML_BACKGROUND=-6;?
13
?static?int?HTML_APPLET=-7;?
14
?
15
boolean?outsideTag=true;?//判斷是否在標(biāo)記之中?
16
?
17
?//構(gòu)造器,定義該令牌流的語法表。?
18
?public?HtmlTokenizer(BufferedReader?r)?
{?
19
super(r);?
20
this.resetSyntax();?//重置語法表?
21
this.wordChars(0,255);?//令牌范圍為全部字符?
22
this.ordinaryChar('<?');?//HTML標(biāo)記兩邊的分割符?
23
this.ordinaryChar('>');?
24
?}?//end?of?constructor?
25
?
26
?public?int?nextHtml()
{?
27
int?token;?//令牌?
28
try
{?
29
switch(token=this.nextToken())
{?
30
case?StreamTokenizer.TT_EOF:?
31
//如果已讀到流的盡頭,則返回TT_EOF?
32
return?HTML_EOF;?
33
case?'<?':?//進(jìn)入標(biāo)記字段?
34
outsideTag=false;?
35
return?nextHtml();?
36
case?'>':?//出標(biāo)記字段?
37
outsideTag=true;?
38
return?nextHtml();?
39
case?StreamTokenizer.TT_WORD:?
40
//若當(dāng)前令牌為單詞,判斷是哪個(gè)標(biāo)記?
41
if?(allWhite(sval))?
42
?return?nextHtml();?//過濾其中空格?
43
else?if(sval.toUpperCase().indexOf("FRAME")?
44
!=-1?&&?!outsideTag)?//標(biāo)記FRAME?
45
?return?HTML_FRAME;?
46
else?if(sval.toUpperCase().indexOf("IMG")?
47
!=-1?&&?!outsideTag)?//標(biāo)記IMG?
48
?return?HTML_IMAGE;?
49
else?if(sval.toUpperCase().indexOf("BACKGROUND")?
50
!=-1?&&?!outsideTag)?//標(biāo)記BACKGROUND?
51
?return?HTML_BACKGROUND;?
52
else?if(sval.toUpperCase().indexOf("APPLET")?
53
!=-1?&&?!outsideTag)?//標(biāo)記APPLET?
54
?return?HTML_APPLET;?
55
default:?
56
System.out.println?("Unknown?tag:?"+token);?
57
return?HTML_UNKNOWN;?
58
?}?//end?of?case?
59
}catch(IOException?e)
{?
60
System.out.println("Error:"+e.getMessage());}?
61
return?HTML_UNKNOWN;?
62
?}?//end?of?nextHtml?
63
?
64
protected?boolean?allWhite(String?s)
{//過濾所有空格?
65
//實(shí)現(xiàn)略?
66
?}//?end?of?allWhite?
67
?
68
}?//end?of?class?
69

?2

?3

?4



?5

?6

?7

?8

?9

10

11

12

13

14

15

16

17

18



19

20

21

22

23

24

25

26



27

28



29



30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59



60

61

62

63

64



65

66

67

68

69
