Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад]

Библиотека примеров приложений Java

Оглавление
Выбор файлов
Простейший редактор текста
Копирование файлов UNICODE
Сохранение объекта Java в файле
Произвольные классы и файлы
Буферизация потоков
Разбор конфигура-
ционного файла

Работа с консолью
Работа с классом PrintWriter
Разбор строк класса String
Загрузка и просмотр изображений
Потоки в оперативной памяти
Конвейерные потоки
Комбинирование двух потоков
Комбинирование нескольких потоков
Поиск слова в текстовом файле
Произвольный доступ к файлу
Информация о файле
Работа с каталогами
Просмотр содержимого каталога
Просмотр каталога с фильтром
Панель для выбора каталога
Список системных свойств
Сохранение списка системных свойств
Контрольная сумма файла
Копирование, переименование, удаление файлов
Архивы ZIP
Создание архива ZIP
Распаковка архива ZIP
Обход дерева каталогов

Назад Вперед

7.7. Разбор конфигурационного файла

На примере приложения, читающего конфигурационный файл со строками вида "ИмяПараметра=Значение" мы показываем способы работы с разборщиком потоков класса StreamTokenizer.

Исходный текст примера

Архив проекта для Java WorkShop 2.0

Немного теории

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

В JDK версии 1.0 класс StreamTokenizer имел один конструктор следующего вида:

StreamTokenizer(InputStream);

С его помощью создается разборщик потока класса InputStream.

Начиная с версии JDK 1.1 вместо него рекомендуется использовать другой конструктор:

StreamTokenizer(InputStreamReader);

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

Методы класса StreamTokenizer

Для настройки параметров разборщика StreamTokenizer и получения отдельных элементов входного потока вы должны пользоваться методами, определенными в классе StreamTokenizer:

public void commentChar(int ch);
public void slashSlashComments(boolean flag);
public void slashStarComments(boolean flag);
public void quoteChar(int ch);
public void eolIsSignificant(boolean flag);
public void lowerCaseMode(boolean fl);
public void ordinaryChar(int ch);
public void ordinaryChars(int low,int hi);
public void resetSyntax();
public void parseNumbers();
public void whitespaceChars(int low, int hi);
public void wordChars(int low, int hi);	

Подробное описание этих методов вы найдете в документации к JDK. Здесь мы приведем только краткую информацию об их назначении.

С помощью метода commentChar вы можете указать символ комментария. Если в строке входного потока попадется такой символ, то он и все следующие за ним до конца текущей строки символы будут проигнорированы.

Методы SlashSlashComments и slashStarComments позволяют указать, что для входного текста используются разделители комментариев в виде двойного символа '/' и '/* … */', соответственно.

Метод quoteChar позволяет задать символ, который будет использован в качестве кавычек. Когда при разборе потока встречаются слова, взятые в кавычки, они возвращаются программе разбора без кавычек.

Если передать методу eolIsSignificant значение true, разделители строк будут интерпретироваться как отдельные элементы.

Метод lowerCaseMode позволяет включить режим, при котором все выделенные элементы будут перекодированы в строчные символы.

Методы ordinaryChar и ordinaryChars позволяют указать символы, которые должны интерпретироваться как обычные, из которых составляются слова или цифры.

С помощью метода resetSyntax вы можете указать, что все символы будут рассматриваться, как обычные.

Метод parseNumbers включает режим разбора чисел, при котором распознаются и преобразуются числа в формате с плавающей десятичной точкой.

Метод whitespaceChars задает диапазон значений для символов-разделителей отдельных слов в потоке.

Метод wordChars позволяет указать символы, которые являются составными частями слов.

Разбор потока

Обычно для разбора потока организуется цикл, в котором вызывается метод nextToken:

public int nextToken();

Этот метод может вернуть одно из следующих значений:

Значение Описание
TT_WORDTT_WORD Из потока было извлечено слово
TT_NUMBERTT_NUMBER Из потока было извлечено численное значение
TT_EOLTT_EOL Обнаружен конец строки. Это значение возвращается только в том случае, если при настройке параметров разборщика был вызван метод eolIsSignficant
TT_EOFTT_EOF Обнаружен конец файла

Если метод nextToken вернул значение TT_EOF, следует завершить цикл разбора входного потока.

Извлечение элементов потока

Для извлечения элементов потока воспользуйтесь полями, определенными в классе StreamTokenizer:

public String sval;
public double nval;
public int ttype;

Если метод nextToken вернул значение TT_WORD, в поле sval содержится извлеченный элемент в виде текстовой строки. В том случае когда из входного потока было извлечено числовое значение, оно будет храниться в поле nval типа double. Обычные символы записываются в поле ttype.

Если в потоке обнаружены слова, взятые в кавычки, то символ кавычки записывается в поле ttype, а слова - в поле sval.

Описание примера

Наше приложение способно прочесть и разобрать конфигурационный файл следующего вида:

# ==================
# Configuration file
# ==================

OS="SunOS"
ProgramVersion=1
Name="Ivan Ivanoff"
A123=345

# ==================
Counter=123456
NickName=ivan
PhoneNumber="not published"

Файл содержит строки комментария, начинающиеся с символа #, пустые строки и параметры, заданные в формате "ИмяПараметра=Значение".

Заметим, что символьные значения параметров могут содержать пробелы. При этом их необходимо заключить в двойные кавычки.

Результат разбора такого файла в нашем приложении показан на рис. 1.

pic1.gif (4290 bytes)

Рис. 1. Результат разбора конфигурационного файла

Как видите, в процессе разбора из файла были удалены пустые строки и строки комментария. Численные значения были преобразованы к типу double.

Рассмотрим самые интересные фрагменты исходного текста приложения.

Метод FileOpen класса FrameWindow

Именно внутри этого метода мы выполняем разбор входного потока.

Прежде всего метод FileOpen отображает на экране стандартную диалоговую панель для выбора разбираемого файла конфигурации:

FileDialog fdlg;
 
fdlg = new FileDialog(this, "Open file", 
  FileDialog.LOAD);
fdlg.show();

szCurrentFilename = fdlg.getDirectory() +
  fdlg.getFile();

Далее он изменяет заголовок главного окна приложения, записывая в него путь к разбираемому файлу, и стирает окно многострочного редактора текста:

setTitle("Parse: " + szCurrentFilename);
    
ta.selectAll();
ta.replaceRange(
  "", 0, ta.getSelectionEnd());

На следующем этапе для выбранного конфигурационного файла предпринимается попытка создания входного потока данных класса FileInputStream:

FileInputStream is = null;
try 
{
  is = 
    new FileInputStream(szCurrentFilename);
}
catch (FileNotFoundException ex)
{
  System.out.println(ex.toString());
}
catch (SecurityException ex)
{
  System.out.println(ex.toString());
}

Далее мы создаем разборщик потока. Здесь мы предлагаем вам попробовать два метода.

Первый метод основан на вызове конструктора JDK версии 1.0:

StreamTokenizer strtok = 
  new StreamTokenizer(is);

Второй метод вы можете использовать, если закроете символами комментария только что приведенные две строки и откроете следующие:

//  Reader r = new BufferedReader(
//    new InputStreamReader(is));
//  StreamTokenizer strtok = 
//    new StreamTokenizer(r);    

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

На следующем этапе мы настраиваем два параметра разборщика потока, задавая символ комментария и символ двойной кавычки:

strtok.commentChar('#');
strtok.quoteChar('"');

Теперь все готово к запуску цикла разбора потока.

Извлеченные значения имени параметра и текстовое представление его значения будут записываться, соответственно, в переменные szParmName и szParmValue:

String szParmName = "";
String szParmValue = "";

Так как в процесе разбора потока могут возникать исключения, связанные с вводом/выводом, мы размещаем весь цикл в теле оператора try-catch:

try 
{
  while(true)
  {
    . . .
  }
}
catch (IOException ex)
{
  System.out.println(ex.toString());
}

Первым делом мы извлекаем из входного потока имя параметра:

if(strtok.nextToken() ==  
  StreamTokenizer.TT_EOF)
  break;

Если при этом будет достигнут конец файла, цикл прерывается.

Далее мы проверяем, что имя параметра представляет собой текстовую строку, а не что-либо еще (не число, например):

if(strtok.ttype ==  StreamTokenizer.TT_WORD)
{
  szParmName = strtok.sval;
}
else
  break;

Извлеченное имя параметра сохраняется в переменной szParmName.

Далее мы проверяем разделитель:

if(strtok.nextToken() == 
  StreamTokenizer.TT_EOF)
  break;
          
if(strtok.ttype != '=')
  break;

При обнаружнеии ошибки цикл прерывается.

На следующем шаге мы извлекаем значение параметра, которое может быть текстовым или цифровым:

if(strtok.nextToken() == 
  StreamTokenizer.TT_EOF)
  break;

Если значение текстовое, мы сохраняем его в переменной szParmValue:

if(strtok.ttype == StreamTokenizer.TT_WORD)
{
  szParmValue = strtok.sval;
}

Если значение текстовое и заключено в двойные кавычки, мы также сохраняем его в переменной szParmValue

else if(strtok.ttype == '"')
{
  szParmValue = strtok.sval;
}

Цифровое значение мы вначале преобразуем к типу Double, а затем - в текстовую строку:

else if(strtok.ttype == 
  StreamTokenizer.TT_NUMBER)
{
  szParmValue = 
    Double.toString(strtok.nval);
}

При появлении во входном потоке каких-либо других элементов мы прекращаем разбор потока, завершая цикл:

else 
  break;

В случае успешного разбора строки конфигурационного файла извлеченное имя параметра и его значение записываются в окно многострочного редактора текта, размещенное в главном окне приложения:

ta.append(szParmName +
  "=" + szParmValue + "\r\n");

После завершения работы цикла мы закрываем входной поток:

try
{
  is.close();
}
catch (IOException ex)
{
  System.out.println(ex.toString());
}

Назад Вперед

[Назад]