StreamTokenizer
Хотя StreamTokenizer
не является производным от InputStream
или OutputStream
, он работает только с InputStream
, поэтому вполне логично включить его в часть библиотеки ввода-вывода.
Класс StreamTokenizer
используется для разделения любого InputStream
на последовательность "токенов" (tokens
). Эти токены фактически представляют собой непрерывные блоки текста, разделенные любым выбранным нами образом. Например, наши токены могут быть словами, разделенными пробелами и знаками препинания.
Ниже приведен простой пример программы, которая считает количество повторений каждого слова в текстовом файле:
//: ОтсортированныйСчетЧастот.java
// Подсчитывает слова в файле,
// выводит результаты в отсортированном виде.
import java.io.*;
import java.util.*;
import c08.*; // Содержит StrSortVector
``````java
class Счетчик {
private int i = 1;
int прочесть() { return i; }
void увеличить() { i++; }
}
публичный класс ОтсортированныеКоличествоСлов {
приватный FileInputStream файл;
приватный StreamTokenizer st;
приватный Hashtable<String, Счетчик> счетчики = новый Hashtable<>();
ОтсортированныеКоличествоСлов(строка имяФайла) бросает FileNotFoundException {
попробуй {
файл = новый FileInputStream(имяФайла);
st = новый StreamTokenizer(файл);
st. обычныйСимвол('.');
st. обычныйСимвол(' ');
st. обычныйСимвол('-');
} catch(FileNotFoundException e) {
System. out. println("Не удалось открыть " + имяФайла);
бросить e;
}
}
вункция очистка() {
попробуй {
файл. закрыть();
} catch(IOException e) {
System. out. println("файл. закрыть() неудачно");
}
}
вункция считатьСлова() {
попробуй {
пока(st. nextToken() != StreamTokenizer. TT_EOF) {
строка s;
переключатель(st. ttype) {
случай StreamTokenizer. TT_EOL:
s = новая строка("EOL");
прекратить;
случай StreamTokenizer. TT_NUMBER:
s = Double. toString(st. nval);
прекратить;
случай StreamTokenizer. TT_WORD:
s = st. sval; // Уже строка
прекратить;
по умолчанию: // одиночный символ в ttype
s = String. valueOf((char)st. ttype);
}
если(счетчики. containsКлюч(s))
(((Счетчик)счетчики. get(s))). увеличить();
иначе
счетчики. put(s, новый Счетчик());
}
} catch(IOException e) {
System. out. println("st. nextToken() неудачно");
}
}
вункция значения() {
вернуть счетчики. элементы();
}
вункция ключи() { вернуть счетчики. ключи(); }
}
``````java
функция полученияСчетчика(строка s) {
вернуть (Counter)счетчики.get(s);
}
функция отсортированныеКлючи() {
функция e = счетчики.ключи();
StrSortVector sv = новый StrSortVector();
пока(e.естьЕщеЭлементы())
sv.добавитьЭлемент((строка)e.следующийЭлемент());
// Этот вызов заставляет произойти сортировку:
вернуть sv.элементы();
}
публичный статический функция основной(строка[] аргументы) {
попробовать {
ОтсортированныеКоличествоСлов wc =
новый ОтсортированныеКоличествоСлов(аргumentы[0]);
wc.считатьСлова();
функция ключи = wc.отсортированныеКлючи();
пока(ключи.естьЕщеЕлементы()) {
строка ключ = (строка)ключи.следующийЭлемент();
System.out.println(ключ + ": "
+ wc.получениеСчетчика(ключ).чтение());
}
wc.очистка();
} catch(исключение e) {
e.printStackTrace();
}
}
}
///:~
Для сортировки результатов следует использовать специальную структуру данных, так как Java версий OnClickListener{1.0} и OnClickListener{1.1} не предоставляют встроенных методов для сортировки. Это можно легко сделать с помощью StrSortVector
, созданной в главе 8 (часть пакета программного обеспечения, созданного в этой главе).
Для открытия файла используется `FileInputStream`. Для преобразования файла в слова из `FileInputStream` создается объект `StreamTokenizer`. Внутри `StreamTokenizer` существует список разделителей по умолчанию, который можно расширять с помощью различных методов. Например, метод `ordinaryChar()` указывает, что данный символ не имеет особого значения, поэтому парсер не будет считать его частью слов, которые он создаёт. Так, вызов `st.ordinaryChar('.')` говорит о том, что точка не станет частью выделенного слова. Подробнее об этом можно найти в онлайн-документации, которая прилагается к Java.
В методе `countWords()`, каждый раз когда из потока берется токен, информация в поле `ttype` используется для определения того, какие действия должны быть выполнены над каждым токеном — поскольку токен может представлять конец строки, число, строку или символ.
Когда токен найден, проверяется наличие этого токена в виде ключа (`Key`) в `Hashtable counts`. Если ответ положительный, соответствующий объект `Counter` увеличивает своё значение, показывая, что ещё один экземпляр данного слова был найден. Если нет, то создается новый объект `Counter`, так как конструктор `Counter` инициализирует его значением 1, что требуется для подсчёта количества слов.Структура данных `SortedWordCount` не является типом `Hashtable`, поэтому она не наследует её. Она выполняет конкретные операции, поэтому хотя методы `keys()` и `values()` должны быть переопределены, это не означает, что следует использовать наследование, так как многие методы `Hashtable` здесь непригодны. Кроме того, некоторые методы (например, `getCounter()` для получения счетчика для определённой строки, а также `sortedKeys()` для создания списка) меняют форму интерфейса `SortedWordCount`. В `main()` мы используем `SortedWordCount`, чтобы открыть и посчитать количество слов в файле — всего за две строки кода. Затем мы извлекаем перечень сортированных ключей (слов) в виде энумератора и используем его для получения каждого ключа вместе с связанным `Count` (количество). Обратите внимание, что необходимо вызвать `cleanup()`, чтобы файл корректно закрывался.
Второй пример использования `StreamTokenizer` будет представлен в главе 17.
## 10.6.1 `StringTokenizer`
Хотя `StringTokenizer` не является частью библиотеки ввода-вывода, он предоставляет функциональность, очень похожую на ту, что предлагается `StreamTokenizer`, поэтому мы рассматриваем его вместе с ним.Основная задача `StringTokenizer` — это возврат каждого токена из строки по одному за раз. Эти токены представляют собой последовательности символов, разделённые табуляцией, пробелами и новой строкой. Таким образом, токены строки `"Where is my cat?"` будут выглядеть как `"Where"`, `"is"`, `"my"` и `"cat?"`. Как и в случае с `StreamTokenizer`, можно указывать `StringTokenizer` на то, как именно следует разделить входные данные. Однако при использовании `StringTokenizer` требуется передать ещё один параметр конструктору, а именно строку-разделитель, которую хотят использовать. Обычно для более сложных операций рекомендуется использовать `StreamTokenizer`.Для запроса следующего токена из объекта `StringTokenizer` используется метод `nextToken()`. Этот метод либо возвращает следующий токен, либо пустую строку (что указывает на отсутствие доступных токенов).
Например, приведенная ниже программа выполняет ограниченный синтаксический анализ, чтобы определить ключевые фразы внутри предложения и понять, указывает ли предложение на радость или печаль.```java
//: AnalyzeSentence.java
// Search for specific sequences within sentences.
import java.util.*;
public class АнализЗапроса {
public static void основной(String[] аргументы) {
анализировать("Я счастлив по поводу этого");
анализировать("Я не счастлив по поводу этого");
анализировать("Я не! Я счастлив");
анализировать("Я печален по поводу этого");
анализировать("Я не печален по поводу этого");
анализировать("Я не! Я печален");
анализировать("Вы счастливы по поводу этого?");
анализировать("Вы печальны по поводу этого?");
анализировать("Это вы! Я счастлив");
анализировать("Это вы! Я печален");
}
static StringTokenizer st;
static void анализировать(String s) {
System.out.println("\nnew sentence >> " + s);
boolean печальное = false;
st = new StringTokenizer(s);
while (st.hasMoreTokens()) {
String token = st.nextToken();
// Look until you find one of the two initial tokens:
if (!(token.equals("Я")) && !(token.equals("Вы")))
continue; // Top of while loop
if (token.equals("Я")) {
String tk2 = st.nextToken();
if (!tk2.equals("являюсь")) // Must be after Я
return; // From while loop
else {
String tk3 = st.nextToken();
if (tk3.equals("печальный")) {
печальное = true;
return; // From while loop
}
if (tk3.equals("не")) {
String tk4 = st.nextToken();
if (tk4.equals("печальный"))
Для анализа каждого входящего строки мы входим в цикл `while`, извлекая токены из этой строки. Обратите внимание на первый оператор if, который выполняет `continue` (возвращает в начало цикла) если токен не равен `"I"` или `"Am"`. Это означает, что токен будет получен только при обнаружении `"I"` или `"Am"`. Возможно, вы захотите использовать `==` вместо метода `equals()`, но это приведёт к неправильному поведению, так как `==` сравнивает ссылки, а `equals()` — содержимое.
Логика оставшейся части метода analyze()
заключается в поиске синтаксической конструкции типа "I am sad"
(я печален), "I am not happy"
(я не счастлив) или "Are you sad?"
(ты печален?). Без оператора break
этот участок кода был бы еще более запутанным. Учтите, что типичный парсер обычно имеет таблицу токенов и использует короткий фрагмент кода для перемещения по этой таблице при чтении новых токенов.
Наконец, следует рассматривать StringTokenizer
как простую и специфическую версию StreamTokenizer
. Тем не менее, если вам требуется анализировать строку и возможности StringTokenizer
кажутся недостаточными, то единственным действием должно быть преобразование строки в поток данных с помощью StringBufferInputStream
, после чего создание более мощного StreamTokenizer
.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )