Microsoft Visual J++. Создание приложений и аплетов на языке Java. Часть 2© Александр Фролов, Григорий ФроловТом 32, М.: Диалог-МИФИ, 1997, 288 стр. Приложение StreamTokenВ приложении StreamToken мы демонстрируем использование класса StreamTokenizer для разбора входного потока. Вначале приложение запрашивает у пользователя строку для разбора, записывая ее в файл. Затем этот файл открывается для чтения буферизованным потоком и разбирается на составные элементы. Каждый такой элемент выводится в отдельной строке, как это показано на рис. 2.6. Рис. 2.6. Разбор входного потока в приложении StreamToken Обратите внимание, что в процессе разбора значение 3.14 было воспринято как числовое, а 3,14 - нет. Это потому, что при настройке разборщика мы указали, что символ ‘.’ является обычным. Исходный текст приложенияИсходный текст приложения StreamToken представлен в листинге 2.5. Листинг 2.5. Файл StreamToken\StreamToken.java // ========================================================= // Разбор входного потока при помощи класса // StreamTokenizer // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ========================================================= import java.io.*; // ========================================================= // Класс StreamToken // Главный класс приложения // ========================================================= public class StreamToken { // ------------------------------------------------------- // main // Метод, получающий управление при запуске приложения // ------------------------------------------------------- public static void main(String args[]) { // Выходной поток DataOutputStream OutStream; // Входной поток DataInputStream InStream; // Массив для ввода строки с клавиатуры byte bKbdInput[] = new byte[256]; // Введенная строка, которая будет записана в поток String sOut; try { // Выводим строку приглашения System.out.println("Enter string to parse..."); // Читаем с клавиатуры строку для записи в файл System.in.read(bKbdInput); // Преобразуем введенные символы в строку типа String sOut = new String(bKbdInput, 0); // Создаем выходной буферизованный поток данных OutStream = new DataOutputStream( new BufferedOutputStream( new FileOutputStream("output.txt"))); // Записываем строку sOut в выходной поток OutStream.writeBytes(sOut); // Закрываем выходной поток OutStream.close(); // Создаем входной буферизованный поток данных InStream = new DataInputStream( new BufferedInputStream( new FileInputStream("output.txt"))); // Создаем объект для разбора потока TokenizerOfStream tos = new TokenizerOfStream(); // Выполняем разбор tos.TokenizeIt(InStream); // Закрываем входной поток InStream.close(); System.out.println("Press <Enter> to terminate..."); System.in.read(bKbdInput); } catch(Exception ioe) { System.out.println(ioe.toString()); } } } // ========================================================= // Класс TokenizerOfStream // Класс для разбора входного потока // ========================================================= class TokenizerOfStream { public void TokenizeIt(InputStream is) { // Создаем разборщик потока StreamTokenizer stok; // Временная строка String str; try { // Создаем разборщик потока stok = new StreamTokenizer(is); // Задаем режим исключения комментариев, // записанных в стиле С++ (два символа '/') stok.slashSlashComments(true); // Указываем , что символ '.' будет обычным символом stok.ordinaryChar('.'); // Запускаем цикл разбора потока, // который будет завершен при достижении // конца потока while(stok.nextToken() != StreamTokenizer.TT_EOF) { // Определяем тип выделенного элемента switch(stok.ttype) { // Если это слово, записываем его во // временную строку case StreamTokenizer.TT_WORD: { str = new String("\nTT_WORD >" + stok.sval); break; } // Если это число, преобразуем его // в строку case StreamTokenizer.TT_NUMBER: { str = "\nTT_NUMBER >" + Double.toString(stok.nval); break; } // Если найден конец строки, // выводим строку End of line case StreamTokenizer.TT_EOL: { str = new String("> End of line"); break; } // Выводим прочие символы default: { if((char)stok.ttype == '"') { str = new String("\nTT_WORD >" + stok.sval); } else str = "> " + String.valueOf((char)stok.ttype); } // Выводим на консоль содержимое временной строки System.out.println(str); } } catch(Exception ioe) { System.out.println(ioe.toString()); } } } Описание исходного текста приложенияПосле ввода строки с клавиатуры и записи ее в файл через поток наше приложение создает входной буферизованный поток, как это показано ниже: InStream = new DataInputStream( new BufferedInputStream( new FileInputStream("output.txt"))); Далее для этого потока создается разборщик, который оформлен в отдельном классе TokenizerOfStream, определенном в нашем приложении: TokenizerOfStream tos = new TokenizerOfStream(); Вслед за этим мы вызываем метод TokenizeIt, определенный в классе TokenizerOfStream, передавая ему в качестве параметра ссылку на входной поток: tos.TokenizeIt(InStream); Метод TokenizeIt выполняет разбор входного потока, отображая результаты разбора на консоли. После выполнения разбора входной поток закрывается методом close: InStream.close(); Самое интересное в нашем приложении связано, очевидно, с классом TokenizerOfStream, поэтому перейдем к его описанию. В этом классе определен только один метод TokenizeIt: public void TokenizeIt(InputStream is) { . . . } Получая в качестве параметра ссылку на входной поток, он прежде всего создает для него разборщик класса StreamTokenizer: StreamTokenizer stok; stok = new StreamTokenizer(is); Настройка параметров разборщика очень проста и сводится к вызовам всего двух методов: stok.slashSlashComments(true); stok.ordinaryChar('.'); Метод slashSlashComments включает режим распознавания комментариев в стиле языка программирования С++, а метод ordinaryChar объявляет символ ‘.’ обычным символом. После настройки запускается цикл разбора входного потока, причем условием завершения цикла является достижение конца этого потока: while(stok.nextToken() != StreamTokenizer.TT_EOF) { . . . } В цикле анализируется содержимое поля ttype, которое зависит от типа элемента, обнаруженного во входном потоке: switch(stok.ttype) { case StreamTokenizer.TT_WORD: { str = new String("\nTT_WORD >" + stok.sval); break; } case StreamTokenizer.TT_NUMBER: { str = "\nTT_NUMBER >" + Double.toString(stok.nval); break; } case StreamTokenizer.TT_EOL: { str = new String("> End of line"); break; } default: { if((char)stok.ttype == '"') str = new String("\nTT_WORD >" + stok.sval); else str = "> " + String.valueOf((char)stok.ttype); } } На слова и численные значения мы реагируем очень просто - записываем их текстовое представление в рабочую переменную str типа String. При обнаружении конца строки в эту переменную записывается строка End of line. Если же обнаружен обычный символ, мы сравниваем его с символом кавычки. При совпадении в переменную str записывается содержимое поля sval, в котором находятся слова, обнаруженные внутри кавычек. Если же обнаруженный символ не является символом кавычки, он преобразуется в строку и записывается в переменную str. В заключении метод выводит строку str в стандартный поток вывода, отображая на консоли выделенный элемент потока: System.out.println(str); |