Клас StreamTokenizer

Поділ вхідного потоку на окремі лексеми зустрічається досить часто, тому пакет javaio містить спеціальний клас StreamTokenizer для виконання найпростішого лексичного аналізу В даний час цей клас повною мірою працює лише з молодшими 8 бітами Unicode, складовими підмножина символів Latin-1, оскільки внутрішній масив класу, який зберігає інформацію про категорії символів, складається тільки з 256 елементів Символи, що перевищують \ u00ff, вважаються алфавітними Хоча в переважній більшості випадків це дійсно так (власне, більша частина символів відноситься до алфавітним), ви, наприклад, не зможете призначити в якості обмежувача символ ?’ (\ u270D) Навіть з урахуванням цієї умови виділення лексем у багатьох випадках відбувається нормально

Щоб виділити лексеми в потоці, слід створити обєкт StreamTokenizer на основі обєкта InputStream і потім встановити параметри аналізу Цикл сканування викликає метод nextToken, який повертає тип наступної лексеми в потоці З деякими типами лексем звязуються значення, що містяться в полях обєкта StreamTokenizer

Даний клас спроектований в першу чергу для аналізу потоків, що містять текст в стилі Java він не універсальний Проте багато файли конфігурації досить схожі на Java і можуть успішно аналізуватися При розробці нових файлів конфігурації або інших даних можна додати їм схожість з текстами на Java, щоб аналізувати їх за допомогою StreamTokenizer і за рахунок цього заощадити зусилля

Коли метод nextToken розпізнає наступну лексему, він повертає її тип і привласнює це ж значення полю ttype Є чотири типи лексем:

TT_WORD: виявлено слово Знайдене слово поміщається в поле sval типу

String

TT_NUMBER: виявлено число Знайдене число поміщається в поле nval типу double Розпізнаються тільки десяткові числа з плаваючою точкою (з десятковою крапкою або без неї) Аналізатор не може розпізнавати 34e79 як число з плаваючою точкою, або 0xffff як шістнадцяткове число

TT_EOL: виявлений кінець рядка TT_EOF: виявлений кінець файлу

Символи вхідного потоку поділяються на спеціальні та ординарні Спеціальними

вважаються символи, які особливим чином обробляються в процесі аналізу, – прогалини, символи, що утворюють числа і слова, і так далі Всі інші символи відносяться до ординарним Якщо наступний символ потоку є ординарним, то тип лексеми збігається з символом Наприклад, якщо в потоці зустрічається символ В і він не є спеціальним, то тип лексеми (і поле ttype) дорівнює еквіваленту символу В в типі int

Як приклад давайте розглянемо реалізацію методу Sumsum Stream з класу

Sum:

static double sumStream(InputStream in) throws IOException { StreamTokenizer nums = new StreamTokenizer(in)

double result = 00

while (numsnextToken() == StreamTokenizerTT_NUMBER)

result +=numsnval

return result

}

Обєкт StreamTokenizer створюється для вихідного потоку, після чого в циклі відбувається читання лексем з потоку Якщо виявлена ​​лексема є числом, то воно додається до накопичує результату Коли числа у вхідному потоці кінчаються, повертається остаточне значення суми

Наведемо ще один приклад Дана програма читає вміст файлу, шукає в ньому атрибути у вигляді пар імя = значення і зберігає їх в обєктах AttributedImpl, описаних в розділі Реалізація інтерфейсів:

public static Attributed readAttrs(String file)

throws IOException

{

FileInputStream fileIn = new FileInputStream(file) StreamTokenizer in = new StreamTokenizer(fileIn) AttributedImpl attrs = new AttributedImpl()

Attr attr = null

incommentChar (#) / / # – Коментар до кінця рядка inordinaryChar (/) / / Раніше був символом коментаря while (innextToken () = StreamTokenizerTT_EOF) {

if (inttype() == StreamTokenizerTT_WORD) {

if (attr = null) {

attrvalueOf(insval)

attr = null / / Використаний

} else {

attr = new Attr(insval)

attrsadd(attr)

}

} else if (inttype == =) {

if (attr == null)

throw new IOException(&quotmisplaced =&quot)

} else {

if (attr == null) / / очікувалося слово

throw new IOException(&quotbad Attr name&quot)

attrvalueOf(new Double(innval))

attr = null

}

}

return attrs

}

У файлі атрибутів символ # використовується для позначення початку коментарів, ігнорованих під час пошуку Програма шукає в потоці строковую лексему, за якої може (хоча й не зобовязаний) слідувати знак =, супроводжуваний рядком або числом Кожен такий атрибут заноситься в обєкт Attr, що додається до набору атрибутів обєкта AttributedImpl Після завершення аналізу файлу повертається набір атрибутів

Задаючи символ # в якості символу коментаря, ми тим самим встановлюємо його категорію Аналізатор розпізнає кілька категорій символів, які визначаються такими методами:

public void wordChars(int low, int hi)

Символи в цьому діапазоні утворюють слова вони можуть входити в лексему типу TT_WORD Допускається багаторазовий виклик цього методу з різними діапазонами Слово складається з одного або декількох символів, що входять в будь-якій їх допустимих діапазонів

public void whitespaceChars(int low, int hi)

Символи в цьому діапазоні є роздільниками При аналізі вони ігноруються їх єдине призначення полягає в поділі лексем – наприклад, двох послідовних слів Як і у випадку wordChars, можна викликати цей метод кілька разів, при цьому обєднання всіх діапазонів визначає набір символів-роздільників

public void ordinaryChar (int ch)

Символ ch є ординарним Ординарний символ при аналізі потоку повертається сам по собі, а не у вигляді лексеми В якості ілюстрації див наведений вище приклад

public void ordinaryChars (int low, int hi) Символи в діапазоні є ординарними public void commentChar (int ch)

Символ ch починає однорядковий коментар – символи від ch до найближчого кінця

рядки вважаються одним довгим роздільником public void quoteChar (int ch)

Пари символів ch є обмежувачами для строкових констант Коли в потоці розпізнається строкова константа, символ ch повертається в якості лексеми, а поле

sval містить тіло рядка (без символів-обмежувачів) При читанні строкових констант обробляються деякі стандартні символи Java в запису з \ (наприклад, \ t), але не всі Рядки, що сприймаються StreamTokenizer, являють собою підмножину рядків Java Особливо жорстку заборону накладається на використання \ xxxx, \ , \ або (на жаль) \ Q, де символ Q збігається з символом-обмежувачем ch У потоці можуть бути присутні кілька різних символів-обмежувачів, але рядки повинні починатися і закінчуватися одним і тим же обмежувачем Іншими словами, рядок, яка починається одним символом-обмежувачем, триває до наступного входження

того ж символу якщо в середині рядка зустрічається інший символ-обмежувач, то він

просто вважається частиною рядка

public void parseNumbers()

Вказує на необхідність виділення чисел з потоку StreamTokenizer видає числа з плаваючою точкою подвійної точності і повертає тип лексеми TT_NUMBER, а значення лексеми поміщається в поле nval Просто відмовитися від пошуку чисел неможливо – для цього доведеться або викликати ordinaryChars для всіх символів, що входять до складу числа (не забудьте про десяткового точці і знаку мінус), або викликати resetSyntax

public void resetSyntax()

Скидає синтаксичну таблицю, в результаті чого всі символи стають ординарними Якщо ви викличете resetSyntax і потім почнете читати потік, то nextToken завжди буде видавати наступний символ потоку, як ніби ви використовуєте метод InputStreamread

Не існує методів для визначення категорії заданого символу або для додавання нових категорій Нижче наведені значення параметрів за замовчуванням для щойно створеного обєкта StreamTokenizer:

wordChars(‘a’, ‘z’) wordChars(‘A’, ‘Z’) wordChars(128 + 32, 255) whitespaceChars(0,  ‘ ‘) commentChar(‘/’) quoteChar(‘&quot’) quoteChar(‘\’’) parseNumbers()

Решта методів керують поведінкою аналізатора:

public void eolIsSignificant(boolean flag)

Якщо значення flag одно true, то кінець рядка є істотним, і nextToken може повертати TT_EOL В іншому випадку кінці рядків вважаються символами-роздільниками і TT_EOL ніколи не повертається Значення за замовчуванням дорівнює false

public void slashStarComments(boolean flag)

Якщо значення flag одно true, аналізатор розпізнає коментарі виду / * .. * / Значення за замовчуванням дорівнює false

public void slashSlashComments(boolean flag)

Якщо значення flag одно true, аналізатор розпізнає коментарі від / / до кінця рядка

Значення за замовчуванням дорівнює false

public void lowerCaseMode(boolean flag)

Якщо значення flag одно true, всі символи в лексемах типу TT_WORD перетворюються в нижній регістр, якщо є відповідний еквівалент (тобто до слова застосовується метод StringtoLowerCase) Значення за замовчуванням дорівнює false

Є також кілька методів загального призначення:

public void pushBack()

Заносить попередню лексему назад в потік Наступний виклик nextToken знову поверне ту ж саму лексему Глибина відкату обмежується однією лексемою кілька послідовних викликів pushBack еквівалентні одному викликом

public int lineno()

Повертає поточний номер рядка Зазвичай це буває корисно для виведення повідомлень про знайдені помилки

public String toString()

Повертає строкове представлення останньої повернутої лексеми, що включає номер рядка

Вправа 116

Напишіть програму, яка отримує вхідні дані у формі імя оператор значення, Де імя – Одне з трьох імен за вашим вибором, оператор дорівнює +, – або =, а значення є числом Застосуйте всі оператори до іменованих величинам, а в кінці роботи програми виведіть всі три значення Ускладнимо завдання – скористайтеся класом Hashtable, який застосовувався при розробці AttributedImpl, щоб можна було працювати з довільною кількістю іменованих величин, не обовязково трьома

Джерело: Арнольд К, Гослінг Д – Мова програмування Java (1997)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*