Перейти к основному содержимому

Руководство по StreamTokenizer

· 4 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

1. Введение

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

2. StreamTokenizer

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

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

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

Обратите внимание, что концы строк обрабатываются как пробелы, а не как отдельные токены, а комментарии в стиле C/C++ по умолчанию не распознаются.

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

  • 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

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

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

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

Поле 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 .