Книга е още в много ранна фаза на написване



страница62/73
Дата25.07.2016
Размер13.53 Mb.
#6732
1   ...   58   59   60   61   62   63   64   65   ...   73

StreamTokenizer


Макар и StreamTokenizer да не е извлечен от InputStream или OutputStream, той работи само с InputStream обекти, така че направо си принадлежи към IO част­та на библиотеката.

Класът StreamTokenizer се използва за разбиването на InputStream по­сле­до­ва­тел­ност от “tokens,” които са парчета текст разделени с нещо по ваш избор. Например вашите токен могат да бъдат думи, а те могат да бъдат раз­де­ле­ни с интервали и пунктуация.

Да видим програма за броенето на появата на думи в текст:

//: c10:SortedWordCount.java

// Counts words in a file, outputs

// results in sorted form.

import java.io.*;

import java.util.*;

import c08.*; // Contains StrSortList
class Counter {

private int i = 1;

int read() { return i; }

void increment() { i++; }

}
public class SortedWordCount {

private FileReader file;

private StreamTokenizer st;

private HashMap counts = new HashMap();

SortedWordCount(String filename)

throws FileNotFoundException {

try {

file = new FileReader(filename);



st = new StreamTokenizer(new BufferedReader(file));

st.ordinaryChar('.');

st.ordinaryChar('-');

} catch(FileNotFoundException e) {

System.out.println(

"Could not open " + filename);

throw e;

}

}



void cleanup() {

try {


file.close();

} catch(IOException e) {

System.out.println(

"file.close() unsuccessful");

}

}

void countWords() {



try {

while(st.nextToken() !=

StreamTokenizer.TT_EOF) {

String s;

switch(st.ttype) {

case StreamTokenizer.TT_EOL:

s = new String("EOL");

break;


case StreamTokenizer.TT_NUMBER:

s = Double.toString(st.nval);

break;

case StreamTokenizer.TT_WORD:



s = st.sval; // Already a String

break;


default: // single character in ttype

s = String.valueOf((char)st.ttype);

}

if(counts.containsKey(s))



((Counter)counts.get(s)).increment();

else


counts.put(s, new Counter());

}

} catch(IOException e) {



System.out.println(

"st.nextToken() unsuccessful");

}

}

Collection values() {



return counts.values();

}

Set keySet() { return counts.keySet(); }



Counter getCounter(String s) {

return (Counter)counts.get(s);

}

Iterator sortedKeys() {



Iterator e = counts.keySet().iterator();

StrSortList sv = new StrSortList();

while(e.hasNext())

sv.add((String)e.next());

// This call forces a sort:

return sv.iterator();

}

public static void main(String[] args) {



try {

SortedWordCount wc =

new SortedWordCount(args[0]);

wc.countWords();

Iterator keys = wc.sortedKeys();

while(keys.hasNext()) {

String key = (String)keys.next();

System.out.println(key + ": "

+ wc.getCounter(key).read());

}

wc.cleanup();



} catch(Exception e) {

e.printStackTrace();

}

}

} ///:~



Има смисъл да бъдат сортирани, но доколкото Java 1.0 и Java 1.1 нямат никакви сор­тировки, това ще трябва да се направи. Лесно става с StrSortList. (Беше съз­да­ден в глава 8 и е част от пакета създаден в нея глава. Помнете че началната ди­ректория на поддиректориите на примерите от тази книга трябва да е на ва­шия "път до класовете" за да може те да се компилират.)

За отваряне на файла се използва FileInputStream , а за превръщането му в то­ке­ни се създава StreamTokenizer от FileInputStream. В StreamTokenizer има спи­сък от разделители по подразбиране, а вие може да добавите още чрез ме­то­ди. Тука е използван ordinaryChar( ) за да каже “Този знак не е интересен за мен,” така че парсерът не го включва изобщо в думите. Например казвайки st.ordinaryChar('.') ще получим точките да не се включват в състава на думите кои­то се разделят. Повече информация може да намерите в онлайн доку­мен­та­ция­та която идва с Java.

В countWords( ) токените се изтеглят един по един от потока, а ttype ин­фор­ма­ция­та се използва за да се реши какво да се прави с всеки, тъй като токенът мо­же да бъде край на файл, число, стринг или единствен знак.

Щом се намери токенът, пита се HashMap counts дали вече го съдържа като ключ. Ако да, съответният Counter обект се инкрементира за да покаже, че е на­мерен още един случай на тази дума. Ако не, нов Counter се създава – и до­кол­кото конструкторът на Counter се инициализира с единица, това отразява и пър­вата поява.



SortedWordCount не е тип HashMap, така че не е наследен. Той изполнява спе­ци­фична функция, та макар и keys( ) и values( ) методите трябва да ги има, това още не значи че ще се използва наследяване понеже много от HashMap ме­то­ди­те не стават тук. Освен това други методи като getCounter( ), които вземат Counter за конкретен String и sortedKeys( ), който дава Iterator, завършват про­мяната на интерфейса на SortedWordCount.

В main( ) може да се види използването на SortedWordCount за отваряне и брое­не на думи във файл – това са точно два реда код. После се извлича Iterator към сортирания списък на ключове (думите) и се използва за измъкване на съот­ветния ключ и броя на срещанията му Count. Забележете че викането на cleanup( )е необходимо за да се осигури затварянето на файла.

Втори пример с използване на StreamTokenizer има в глава 17.

StringTokenizer


Макар и да не е част от IO библиотеката, StringTokenizer има достатъчно по­доб­на на StreamTokenizer функционалност така че ще се опише тук.

StringTokenizer връща токените в стринг един по един. Тези токени са по­сле­до­ва­телни знаци разделени с табулации, интервали и знаци за нов ред. Така то­ке­ни­те в стринга “Where is my cat?” са “Where”, “is”, “my” и “cat?” Като при StreamTokenizer,вие може да поръчате на StringTokenizer да разделя входния стринг по всякакъв желан начин, но при StringTokenizer това се прави чрез да­ва­не на втори аргумент на конструктора, който е String от желаните раз­де­ли­те­ли. Изобщо, ако ви трябва по-усъвършенстван начин, използвайте StreamTokenizer.

Следващия токен се иска от StringTokenizer обекта чрез метода nextToken( ), кой­то връща или токена, или празен стринг за да покаже че няма повече токени.

Като пример следната програма изпълнява ограничен анализ на изречение, оглеж­дайки за ключови фрази за да разбере дали се предполага радост или тъга.

//: c10:AnalyzeSentence.java

// Look for particular sequences

// within sentences.

import java.util.*;
public class AnalyzeSentence {

public static void main(String[] args) {

analyze("I am happy about this");

analyze("I am not happy about this");

analyze("I am not! I am happy");

analyze("I am sad about this");

analyze("I am not sad about this");

analyze("I am not! I am sad");

analyze("Are you happy about this?");

analyze("Are you sad about this?");

analyze("It's you! I am happy");

analyze("It's you! I am sad");

}

static StringTokenizer st;



static void analyze(String s) {

prt("\nnew sentence >> " + s);

boolean sad = false;

st = new StringTokenizer(s);

while (st.hasMoreTokens()) {

String token = next();

// Look until you find one of the

// two starting tokens:

if(!token.equals("I") &&

!token.equals("Are"))

continue; // Top of while loop

if(token.equals("I")) {

String tk2 = next();

if(!tk2.equals("am")) // Must be after I

break; // Out of while loop

else {


String tk3 = next();

if(tk3.equals("sad")) {

sad = true;

break; // Out of while loop

}

if (tk3.equals("not")) {



String tk4 = next();

if(tk4.equals("sad"))

break; // Leave sad false

if(tk4.equals("happy")) {

sad = true;

break;


}

}

}



}

if(token.equals("Are")) {

String tk2 = next();

if(!tk2.equals("you"))

break; // Must be after Are

String tk3 = next();

if(tk3.equals("sad"))

sad = true;

break; // Out of while loop

}

}



if(sad) prt("Sad detected");

}

static String next() {



if(st.hasMoreTokens()) {

String s = st.nextToken();

prt(s);

return s;



}

else


return "";

}

static void prt(String s) {



System.out.println(s);

}

} ///:~



За да бъде анализиран всеки стринг се влиза в while цикъл и токените се из­важ­дат от стринга. Забележете първия if оператор, който казва да continue (да оти­де в началото на цикъла и да влезе в него пак) ако токенът на е нито “I” нито “Are.” Това значи че ще взимате токени досато биват намирани “I” или “Are”. Бих­те могли да искате да използвате == вместо equals( ) метода, но това не би ра­ботило коректно, понеже == сравнява стойности на манипулатори, докато equals( ) сравнява съдържанията.

Логиката на останалата част от метода analyze( ) е че се търси за “I am sad,” “I am not happy” или “Are you sad?” Без break оператора кодът би бил даже по-обър­кан отколкото е. Така че да сте предупредени че типичен парсер (този е еле­ментарен пример за такъв) нормално има таблица за тези токени и код който се движи по нея, като си променя състоянието в зависимост от появата на нови то­кени .

Ще считате StringTokenizer само за кратък път към един опростен StreamTokenizer. Обаче ако имате String който искате да разделите и StringTokenizer е твърде ограничен, всичко което трябва да направите е да го пре­върната в поток със StringBufferInputStream и после да създадете с него мно­го по-мощния StreamTokenizer.




Сподели с приятели:
1   ...   58   59   60   61   62   63   64   65   ...   73




©obuch.info 2024
отнасят до администрацията

    Начална страница