«1. Введение

В этом руководстве мы покажем, как преобразовать поток символов в токены с помощью класса Java StreamTokenizer.

2. StreamTokenizer

Класс StreamTokenizer считывает поток посимвольно. Каждый из них может иметь ноль или более следующих атрибутов: пробел, буква, цифра, строковая кавычка или символ комментария.

Теперь нам нужно понять конфигурацию по умолчанию. У нас есть следующие типы символов:

    Словесные символы: диапазоны от «a» до «z» и от «A» до «Z». Числовые символы: 0,1,…,9. Пробельные символы: значения ASCII от 0 до 32. Символ комментария: / Строковые кавычки: « и «. комментарии не распознаются по умолчанию.

Этот класс имеет набор важных полей:

TT_EOF — константа, указывающая конец потока TT_EOL — константа, указывающая конец строки TT_NUMBER — константа, указывающая числовой маркер TT_WORD — Константа, указывающая токен слова

    3. Конфигурация по умолчанию

Здесь мы собираемся создать пример, чтобы понять механизм StreamTokenizer. Мы начнем с создания экземпляра этого класса, а затем вызовем метод nextToken(), пока он не вернет значение TT_EOF:

Тестовый файл просто содержит:

private static final int QUOTE_CHARACTER = '\'';
private static final int DOUBLE_QUOTE_CHARACTER = '"';

public static List<Object> streamTokenizerWithDefaultConfiguration(Reader reader) throws IOException {
    StreamTokenizer streamTokenizer = new StreamTokenizer(reader);
    List<Object> tokens = new ArrayList<Object>();

    int currentToken = streamTokenizer.nextToken();
    while (currentToken != StreamTokenizer.TT_EOF) {

        if (streamTokenizer.ttype == StreamTokenizer.TT_NUMBER) {
            tokens.add(streamTokenizer.nval);
        } else if (streamTokenizer.ttype == StreamTokenizer.TT_WORD
            || streamTokenizer.ttype == QUOTE_CHARACTER
            || streamTokenizer.ttype == DOUBLE_QUOTE_CHARACTER) {
            tokens.add(streamTokenizer.sval);
        } else {
            tokens.add((char) currentToken);
        }

        currentToken = streamTokenizer.nextToken();
    }

    return tokens;
}

Теперь, если мы распечатаем содержимое массива мы увидим:

3 quick brown foxes jump over the "lazy" dog!
#test1
//test2

Чтобы лучше понять пример, нам нужно объяснить поля StreamTokenizer.ttype, StreamTokenizer.nval и StreamTokenizer.sval.

Number: 3.0
Word: quick
Word: brown
Word: foxes
Word: jump
Word: over
Word: the
Word: lazy
Word: dog
Ordinary char: !
Ordinary char: #
Word: test1

Поле ttype содержит тип только что прочитанной лексемы. Это может быть TT_EOF, TT_EOL, TT_NUMBER, TT_WORD. Однако для маркера строки в кавычках его значением является значение ASCII символа кавычек. Более того, если токен представляет собой обычный символ, такой как «!», без атрибутов, то ttype будет заполнен значением ASCII этого символа.

Далее мы используем поле sval для получения токена, только если это TT_WORD, то есть токен слова. Но если мы имеем дело со строковым токеном в кавычках, скажем, «ленивый», тогда это поле содержит тело строки.

Наконец, мы использовали поле nval для получения токена, только если это числовой токен, используя TT_NUMBER.

4. Пользовательская конфигурация

Здесь мы изменим конфигурацию по умолчанию и создадим еще один пример.

Во-первых, мы собираемся установить несколько дополнительных символов слова, используя метод wordChars(int low, int hi). Затем мы сделаем символ комментария (‘/’) обычным и продвигаем ‘#’ как новый символ комментария.

Наконец, мы рассмотрим конец строки как символ токена с помощью метода eolIsSignificant(boolean flag).

Нам нужно только вызвать эти методы для объекта streamTokenizer:

И здесь у нас есть новый вывод:

public static List<Object> streamTokenizerWithCustomConfiguration(Reader reader) throws IOException {
    StreamTokenizer streamTokenizer = new StreamTokenizer(reader);
    List<Object> tokens = new ArrayList<Object>();

    streamTokenizer.wordChars('!', '-');
    streamTokenizer.ordinaryChar('/');
    streamTokenizer.commentChar('#');
    streamTokenizer.eolIsSignificant(true);

    // same as before

    return tokens;
}

Обратите внимание, что двойные кавычки стали частью токена, символ новой строки больше не пробельный символ, а обычный символ и, следовательно, односимвольный токен.

// same output as earlier
Word: "lazy"
Word: dog!
Ordinary char: 

Ordinary char: 

Ordinary char: /
Ordinary char: /
Word: test2

Кроме того, символы, следующие за символом «#», теперь пропускаются, а «/» является обычным символом.

Мы также можем изменить символ кавычки с помощью метода quoteChar(int ch) или даже символы пробела, вызвав метод whitespaceChars(int low, int hi). Таким образом, можно выполнять дальнейшие настройки, вызывая методы StreamTokenizer в различных комбинациях.

5. Заключение

В этом руководстве мы увидели, как разобрать поток символов на токены с помощью класса StreamTokenizer. Мы узнали о механизме по умолчанию и создали пример с конфигурацией по умолчанию.

Наконец, мы изменили параметры по умолчанию и заметили, насколько гибок класс StreamTokenizer.

Как обычно, код можно найти на GitHub.

«