35.JAVA编程思想——JAVA IO StreamTokenizer

来源:互联网 发布:凉凉网络用语 编辑:程序博客网 时间:2024/05/22 16:44

35.JAVA编程思想——JAVA IO StreamTokenizer

尽管StreamTokenizer并不是从 InputStream或 OutputStream衍生的,但它只随同InputStream工作,所以十分恰当地包括在库的 IO部分中。

StreamTokenizer类用于将任何InputStream分割为一系列“记号”(Token)。这些记号实际是一些断续的文本块,中间用我们选择的任何东西分隔。例如,我们的记号可以是单词,中间用空白(空格)以及标点符号分隔。

由于Java 1.0 和 Java 1.1 都没有提供任何排序方法,所以必须由自己动手。这个目标可用一个 StrSortVector 方便地达成(创建于第 8章,属于那一章创建的软件包的一部分。记住本书所有子目录的起始目录都必须位于类路径中,否则程序将不能正确地编译)。

为打开文件,使用了一个FileInputStream。而且为了将文件转换成单词,从 FileInputStream中创建了一个StreamTokenizer。在StreamTokenizer 中,存在一个默认的分隔符列表,我们可用一系列方法加入更多的分隔符。在这里,我们用ordinaryChar()指出“该字符没有特别重要的意义”,所以解析器不会把它当作自己创建的任何单词的一部分。例如,st.ordinaryChar('.')表示小数点不会成为解析出来的单词的一部分。在与Java 配套提供的联机文档中,可以找到更多的相关信息。

在countWords()中,每次从数据流中取出一个记号,而ttype信息的作用是判断对每个记号采取什么操作——因为记号可能代表一个行尾、一个数字、一个字串或者一个字符。找到一个记号后,会查询Hashtable counts,核实其中是否已经以“键”(Key)的形式包含了一个记号。若答案是肯定的,对应的Counter(计数器)对象就会增值,指出已找到该单词的另一个实例。若答案为否,则新建一个Counter——因为 Counter构建器会将它的值初始化为1,正是我们计算单词数量时的要求。

SortedWordCount并不属于Hashtable(散列表)的一种类型,所以它不会继承。它执行的一种特定类型的操作,所以尽管keys()和values()方法都必须重新揭示出来,但仍不表示应使用那个继承,因为大量Hashtable 方法在这里都是不适当的。除此以外,对于另一些方法来说(比如getCounter()——用于获得一个特定字串的计数器;又如sortedKeys()——用于产生一个枚举),它们最终都改变了 SortedWordCount 接口的形式。

在main()内,我们用 SortedWordCount打开和计算文件中的单词数量——总共只用了两行代码。随后,我们为一个排好序的键(单词)列表提取出一个枚举。并用它获得每个键以及相关的Count(计数)。注意必须调用cleanup(),否则文件不能正常关闭。

StringTokenizer

尽管并不必要IO 库的一部分,但StringTokenizer提供了与 StreamTokenizer极相似的功能,所以在这里一并讲述。

StringTokenizer的作用是每次返回字串内的一个记号。这些记号是一些由制表站、空格以及新行分隔的连续字符。因此,字串“Where is my cat?”的记号分别是“Where”、“is”、“my”和“cat?”。与StreamTokenizer类似,我们可以指示 StringTokenizer 按照我们的愿望分割输入。但对于StringTokenizer,却需要向构建器传递另一个参数,即我们想使用的分隔字串。通常,如果想进行更复杂的操作,应使用StreamTokenizer。

可用nextToken()向 StringTokenizer 对象请求字串内的下一个记号。该方法要么返回一个记号,要么返回一个空字串(表示没有记号剩下)。

作为一个例子,下述程序将执行一个有限的句法分析,查询键短语序列,了解句子暗示的是快乐亦或悲伤的含义。

代码

import java.util.*;

publicclassAnalyzeSentence {

publicstaticvoidmain(String[]args){

analyze("I am happy about this");

analyze("I am not happy about this");

analyze("I am not! I am happy");

analyze("I am sad about this");

analyze("I am not sad about this");

analyze("I am not! I am sad");

analyze("Are you happy about this?");

analyze("Are you sad about this?");

analyze("It's you! I am happy");

analyze("It's you! I am sad");

}

    static StringTokenizerst;

    staticvoid analyze(String s) {

        prt("\nnew sentence >> " +s);

        booleansad = false;

        st =new StringTokenizer(s);

        while (st.hasMoreTokens()) {

            Stringtoken=next();

            // Look until you find one of the

            // two starting tokens:

            if (!token.equals("I") && !token.equals("Are"))

                continue;// Top of while loop

            if (token.equals("I")) {

                Stringtk2=next();

                if (!tk2.equals("am"))// Must be after I

                    break;// Out of while loop

                else {

                    Stringtk3=next();

                    if (tk3.equals("sad")) {

                        sad = true;

                        break;// Out of while loop

                    }

                    if (tk3.equals("not")) {

                        Stringtk4=next();

                        if (tk4.equals("sad"))

                            break; // Leave sad false

                        if (tk4.equals("happy")){

                            sad = true;

                            break;

                        }

                    }

                }

            }

            if (token.equals("Are")) {

                Stringtk2=next();

                if (!tk2.equals("you"))

                    break;// Must be after Are

                Stringtk3=next();

                if (tk3.equals("sad"))

                    sad = true;

                break;// Out of while loop

            }

        }

        if (sad)

            prt("Sad detected");

    }

    static String next() {

        if (st.hasMoreTokens()) {

            Strings =st.nextToken();

            prt(s);

            returns;

        }else

            return"";

    }

    staticvoid prt(String s) {

        System.out.println(s);

    }

} /// :~

执行

new sentence >> I am happyabout this

I

am

happy

about

this

 

new sentence >> I am not happyabout this

I

am

not

happy

Sad detected

 

new sentence >> I am not! I amhappy

I

am

not!

I

am

happy

 

new sentence >> I am sad aboutthis

I

am

sad

Sad detected

 

new sentence >> I am not sadabout this

I

am

not

sad

 

new sentence >> I am not! I amsad

I

am

not!

I

am

sad

Sad detected

 

new sentence >> Are you happyabout this?

Are

you

happy

 

new sentence >> Are you sadabout this?

Are

you

sad

Sad detected

 

new sentence >> It's you! I amhappy

It's

you!

I

am

happy

 

new sentence >> It's you! I amsad

It's

you!

I

am

sad

Sad detected

对于准备分析的每个字串,我们进入一个while 循环,并将记号从那个字串中取出。请注意第一个 if 语句,假如记号既不是“I”,也不是“Are”,就会执行 continue(返回循环起点,再一次开始)。这意味着除非发现一个“I”或者“Are”,才会真正得到记号。大家可能想用==代替equals()方法,但那样做会出现不正常的表现,因为==比较的是句柄值,而 equals()比较的是内容。

analyze()方法剩余部分的逻辑是搜索“I am sad”(我很忧伤、“I amnothappy”(我不快乐)或者“Are you sad?”(你悲伤吗?)这样的句法格式。若没有break 语句,这方面的代码甚至可能更加散乱。大家应注意对一个典型的解析器来说,通常都有这些记号的一个表格,并能在读取新记号的时候用一小段代码在表格内移动。

无论如何,只应将StringTokenizer 看作StreamTokenizer 一种简单而且特殊的简化形式。然而,如果有一个字串需要进行记号处理,而且StringTokenizer 的功能实在有限,那么应该做的全部事情就是用StringBufferInputStream 将其转换到一个数据流里,再用它创建一个功能更强大的StreamTokenizer。

 

0 0