ZetCode

Java StreamTokenizer 类

最后修改时间:2025 年 4 月 16 日

java.io.StreamTokenizer 类将输入流解析成标记。它可以识别数字、带引号的字符串和各种注释风格。这个类对于简单的词法分析任务很有用。

StreamTokenizer 将输入分解成标记,这些标记可以是单词、数字、字符串或特殊字符。它提供了配置哪些应被视为空白、注释或单词字符的方法。该类不是线程安全的。

StreamTokenizer 类概述

StreamTokenizerInputStreamReader 作为输入。它将标记分类为 TT_WORDTT_NUMBERTT_EOF 等类型。该类提供了配置标记识别规则的方法。

public class StreamTokenizer {
    public StreamTokenizer(InputStream in);
    public StreamTokenizer(Reader r);
    public void resetSyntax();
    public void wordChars(int low, int hi);
    public void whitespaceChars(int low, int hi);
    public void ordinaryChars(int low, int hi);
    public void ordinaryChar(int ch);
    public void commentChar(int ch);
    public void quoteChar(int ch);
    public void parseNumbers();
    public void eolIsSignificant(boolean flag);
    public void slashStarComments(boolean flag);
    public void slashSlashComments(boolean flag);
    public void lowerCaseMode(boolean fl);
    public int nextToken();
    public int ttype;
    public String sval;
    public double nval;
    public int lineno();
}

上面的代码展示了 StreamTokenizer 的关键方法和字段。nextToken 方法读取下一个标记。标记类型存储在 ttype 中,字符串和数值存储在 svalnval 中。

创建 StreamTokenizer

StreamTokenizer 可以从 InputStream 或 Reader 创建。 Reader 版本更适合字符流处理。在使用之前,该类需要配置才能定义标记识别规则。

Main.java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.StreamTokenizer;

public class Main {

    public static void main(String[] args) {
        try {
            // Create from Reader
            BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
            StreamTokenizer tokenizer1 = new StreamTokenizer(reader);
            
            // Basic configuration
            tokenizer1.wordChars('a', 'z');
            tokenizer1.wordChars('A', 'Z');
            tokenizer1.whitespaceChars(' ', ' ');
            tokenizer1.whitespaceChars('\n', '\n');
            tokenizer1.whitespaceChars('\r', '\r');
            tokenizer1.whitespaceChars('\t', '\t');
            
            System.out.println("StreamTokenizer created and configured");
            
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了如何从 Reader 创建 StreamTokenizer。我们将其配置为将字母识别为单词字符和常见的空白字符。完成后,始终关闭底层流。配置是标记化的必要条件。

基本标记化示例

StreamTokenizer 的最简单用法是读取标记,直到文件结束。每次调用 nextToken 都会返回标记类型。标记值存储在 sval(对于单词)或 nval(对于数字)中。

Main.java
import java.io.StringReader;
import java.io.StreamTokenizer;

public class Main {

    public static void main(String[] args) {
        String input = "Hello 123 World 45.67";
        StringReader reader = new StringReader(input);
        StreamTokenizer tokenizer = new StreamTokenizer(reader);
        
        try {
            tokenizer.parseNumbers();
            
            while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
                switch (tokenizer.ttype) {
                    case StreamTokenizer.TT_WORD:
                        System.out.println("Word: " + tokenizer.sval);
                        break;
                    case StreamTokenizer.TT_NUMBER:
                        System.out.println("Number: " + tokenizer.nval);
                        break;
                    default:
                        System.out.println("Other: " + (char) tokenizer.ttype);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例将包含单词和数字的简单字符串进行标记化。parseNumbers 方法启用数字识别。 switch 语句处理不同的标记类型。TT_EOF 表示输入流结束。

处理带引号的字符串

当使用 quoteChar 配置时,StreamTokenizer 可以识别带引号的字符串。整个带引号的字符串成为一个单独的标记。单引号和双引号都可以配置为引号字符。

Main.java
import java.io.StringReader;
import java.io.StreamTokenizer;

public class Main {

    public static void main(String[] args) {
        String input = "Name 'John Doe' Age 25 City \"New York\"";
        StringReader reader = new StringReader(input);
        StreamTokenizer tokenizer = new StreamTokenizer(reader);
        
        try {
            // Configure single and double quotes as quote characters
            tokenizer.quoteChar('\'');
            tokenizer.quoteChar('"');
            
            while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
                if (tokenizer.ttype == '\'' || tokenizer.ttype == '"') {
                    System.out.println("Quoted string: " + tokenizer.sval);
                } else if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
                    System.out.println("Word: " + tokenizer.sval);
                } else if (tokenizer.ttype == StreamTokenizer.TT_NUMBER) {
                    System.out.println("Number: " + tokenizer.nval);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例演示了处理单引号和双引号字符串。带引号的内容可在 sval 中找到。引号字符本身作为标记类型返回。这允许区分不同的引号样式。

自定义标记识别

StreamTokenizer 提供了广泛的自定义,以确定构成标记的内容。您可以将字符范围定义为单词字符、空白或普通字符。resetSyntax 方法清除所有先前的设置。

Main.java
import java.io.StringReader;
import java.io.StreamTokenizer;

public class Main {

    public static void main(String[] args) {
        String input = "user@example.com 192.168.1.1 #comment";
        StringReader reader = new StringReader(input);
        StreamTokenizer tokenizer = new StreamTokenizer(reader);
        
        try {
            // Reset and customize syntax
            tokenizer.resetSyntax();
            tokenizer.wordChars('a', 'z');
            tokenizer.wordChars('A', 'Z');
            tokenizer.wordChars('0', '9');
            tokenizer.wordChars('@', '@');
            tokenizer.wordChars('.', '.');
            tokenizer.whitespaceChars(' ', ' ');
            tokenizer.commentChar('#');
            
            while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
                if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
                    System.out.println("Token: " + tokenizer.sval);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例展示了自定义语法配置。我们将“@”和“.”作为单词字符来处理电子邮件和 IP。 “#”是注释字符。在 resetSyntax 之后,所有字符都是“普通字符”,直到配置为止。

跟踪行号

StreamTokenizer 可以在解析期间跟踪行号。lineno 方法返回当前的行号。这对于源代码处理中的错误报告很有用。

Main.java
import java.io.StringReader;
import java.io.StreamTokenizer;

public class Main {

    public static void main(String[] args) {
        String input = "First line\nSecond line\nThird line";
        StringReader reader = new StringReader(input);
        StreamTokenizer tokenizer = new StreamTokenizer(reader);
        
        try {
            tokenizer.eolIsSignificant(true);
            
            while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
                if (tokenizer.ttype == StreamTokenizer.TT_EOL) {
                    System.out.println("End of line " + tokenizer.lineno());
                } else if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
                    System.out.println("Word at line " + 
                        tokenizer.lineno() + ": " + tokenizer.sval);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例演示了行号跟踪。eolIsSignificant(true) 使行尾标记具有意义。lineno 方法有助于识别标记位置。这对于编译器或解析器实现很有价值。

处理注释

StreamTokenizer 支持 C 风格(/* */)和 C++ 风格(//)的注释。slashStarCommentsslashSlashComments 方法控制此行为。注释内容在标记化期间被跳过。

Main.java
import java.io.StringReader;
import java.io.StreamTokenizer;

public class Main {

    public static void main(String[] args) {
        String input = "code /* comment */ more // line comment\nend";
        StringReader reader = new StringReader(input);
        StreamTokenizer tokenizer = new StreamTokenizer(reader);
        
        try {
            tokenizer.slashStarComments(true);
            tokenizer.slashSlashComments(true);
            
            while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
                if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
                    System.out.println("Token: " + tokenizer.sval);
                } else if (tokenizer.ttype == StreamTokenizer.TT_EOL) {
                    System.out.println("End of line");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例展示了 StreamTokenizer 中的注释处理。两种注释样式都被启用。标记器自动跳过注释内容。只有非注释标记由 nextToken 返回。

来源

Java StreamTokenizer 类文档

在本文中,我们介绍了 Java StreamTokenizer 类的基本方法和特性。理解这些概念对于 Java 应用程序中的文本解析和词法分析至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员,在编程领域拥有多年经验。我从 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和 8 本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程