Назад
Вперед
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.
Рис. 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());
}
Назад Вперед |