Журнал LinuxFormat - перейти на главную

LXF86:Java

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
(Файлы — потоки)
м (восстановление кавычек в коде AWB)
 
(не показаны 15 промежуточных версий 3 участников)
Строка 1: Строка 1:
 +
{{Цикл/Java}}
 +
 
=== Хранение данных ===
 
=== Хранение данных ===
 
''ЧАСТЬ 3: Даже самой замечательной программе надо откуда-то черпать данные для своей работы. Данные, как известно, хранятся в файлах. Тему продолжает '''Антон Черноусов'''.''
 
''ЧАСТЬ 3: Даже самой замечательной программе надо откуда-то черпать данные для своей работы. Данные, как известно, хранятся в файлах. Тему продолжает '''Антон Черноусов'''.''
Строка 7: Строка 9:
 
XML-данными.
 
XML-данными.
  
=== Файлы — потоки ===
+
=== Файлы — потоки ===
 
Сказочное королевство под руководством царевны Несмеяны (так как cупруг практически всегда отсутствовал), благодаря талантам и приобретенным навыкам, стало разрастаться, и результаты полюдья просто-напросто перестали помещаться в семейный чулан. Чтобы накапливать и хранить богатства, потребовались дополнительные помещения, роль которых для нас привычно играют файлы.
 
Сказочное королевство под руководством царевны Несмеяны (так как cупруг практически всегда отсутствовал), благодаря талантам и приобретенным навыкам, стало разрастаться, и результаты полюдья просто-напросто перестали помещаться в семейный чулан. Чтобы накапливать и хранить богатства, потребовались дополнительные помещения, роль которых для нас привычно играют файлы.
  
Отношение к файлам в Java достаточно непростое: если рассматривать файл как устройство для ввода/вывода информации — с этой точ
+
Отношение к файлам в Java достаточно непростое: если рассматривать файл как устройство для ввода/вывода информации — с этой точ
 
ки зрения он подобен блоку памяти или экрану, интерфейс доступа к которому унифицирован: это поток. Но несмотря на унифицированный интерфейс, существует большое количество классов сходной функциональности, в которых легко запутаться.
 
ки зрения он подобен блоку памяти или экрану, интерфейс доступа к которому унифицирован: это поток. Но несмотря на унифицированный интерфейс, существует большое количество классов сходной функциональности, в которых легко запутаться.
  
Строка 18: Строка 20:
  
 
=== Чтение данных ===
 
=== Чтение данных ===
 +
Разнообразие классов для работы с файлами позволяет выбрать для себя ту связку, которая больше нравится. Лично я для чтения данных использую BufferedReader, InputStreamReader и FileInputStream. Собственно взаимодействие этих классов для программиста заканчивается в момент создания экземпляра BufferedReader, что делается следующим образом:
 +
 +
<source lang = "java">
 +
BufferedReader br = null;
 +
br = new BufferedReader(new InputStreamReader(new FileInputStream(pathToFile), encoding));
 +
</source>
 +
 +
В процессе создания участвуют строковые переменные, содержащие путь к файлу и кодировку, в которой производится считывание
 +
данных: pathToFile и encoding. В классах, работающих с файлами, считается обязательным создавать переменную для кодировки по умолчанию:
 +
 +
<source lang = "java">
 +
protected static String DEFAULT_ENCODING = "UTF-8";
 +
</source>
 +
 +
Любой текстовый файл можно представить себе в виде набора строк, поэтому давайте реализуем метод для считывания содержимого
 +
файла в массив String[]. Далее представлен метод rippedCurrentFile(path ToFile, encoding) класса FileRipper, который извлекает данные из файла с помощью метода readLine() экземпляра класса BufferedReader:
 +
 +
<source lang = "java">
 +
protected boolean rippedCurrentFile(String pathToFile, String encoding) {
 +
  // connecting to file
 +
  BufferedReader br = null;
 +
  try {
 +
    br = new BufferedReader(new InputStreamReader(new FileInputStream(pathToFile),encoding));
 +
  }
 +
  catch (UnsupportedEncodingException e) {
 +
    this.error = FILE_ERROR_UNSUPPORTED_ENCODING; return false;
 +
  }
 +
  catch (FileNotFoundException e) {
 +
    this.error = FILE_ERROR_NO_FILE; return false;
 +
  }
 +
  // ripping the file
 +
  String str = null;
 +
  ArrayList allStrings = new ArrayList();
 +
  try {
 +
    while (!(str = br.readLine()).equals(null)) { allStrings.add(str); }
 +
  }
 +
  catch (IOException e) {
 +
    this.error = FILE_ERROR_IO_READ; return false;
 +
  }
 +
  catch (NullPointerException e) {
 +
    this.error = FILE_ERROR_END_OF_FILE;
 +
  }
 +
  // free the resources
 +
  try {
 +
    br.close();
 +
  }
 +
  catch (IOException e) {
 +
    this.error = FILE_ERROR_IO_CLOSE; return false;
 +
  }
 +
  this.allStrings = (String[]) allStrings.toArray(new String[0]);
 +
  return true;
 +
}
 +
 +
</source>
 +
Полный код примера, в том числе код класса ConsoleToFileRipper, применяющий экземпляр класса FileRipper для извлечения данных из файла, можно найти на диске в каталоге '''examples 1'''.
  
 
=== Запись данных ===
 
=== Запись данных ===
 +
Процесс записи данных в файл хоть и отличается от чтения, но тоже достаточно похож на организацию конвейера. Для записи
 +
я обычно использую связку BufferedWriter, OutputStreamWriter, FileOutputStream.
 +
<source lang = "java">
 +
BufferedWriter out;
 +
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(pathToFile), encoding));
 +
</source>
 +
Для освобождения ресурсов, которые используют экземпляры классов BufferedReader и BufferedWriter, необходимо вызвать метод
 +
close().
 +
 +
Далее приведу простой пример метода, который записывает строковый массив в файл (полный код метода расположен на диске в
 +
директории '''examples 2'''):
 +
<source lang = "java">
 +
public boolean createCurrentFile(String pathToFile, String[] allStrings, String encoding){
 +
  try {
 +
    BufferedWriter out;
 +
    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(pathToFile), encoding));
 +
    for (int i = 0; i < allStrings.length; i++) {
 +
      out.write(allStrings[i]); out.write('\n');
 +
    }
 +
    out.close();
 +
  }
 +
  catch (IOException e) {
 +
    e.printStackTrace();
 +
    return false;
 +
  }
 +
  return true;
 +
}
 +
</source>
  
 
=== Свободный доступ ===
 
=== Свободный доступ ===
 +
В представленных ранее примерах доступ к данным осуществляется последовательно, что не всегда удобно (хотя в большинстве случаев именно такой доступ и используется). Для осуществления чтения и записи данных из файла в произвольном порядке существует специальный класс RandomAccessFile, экземпляр которого создается следующим образом:
 +
<source lang = "java">
 +
RandomAccessFile raf = new RandomAccessFile(pathToFile, mode);
 +
</source>
 +
При этом pathToFile — путь до файла, а mode — режим работы. mode может принимать значения: r (только чтение), rw (чтение-запись), rws (чтение-запись с синхронным сохранением содержимого и метаданных), rwd (чтение-запись с синхронным сохранением содержимого файла). К сожалению, кодировку указать нельзя. Огромным преимуществом подхода является то, что с помощью метода getFilePointer() можно узнать текущее месторасположение указателя, а с помощью метода seek() можно передвинуть указатель в необходимое место в файле. Я предпочитаю не использовать данный класс — считайте это личным предубеждением.
  
 
=== Протоколирование работы программы ===
 
=== Протоколирование работы программы ===
 +
Для контроля и анализа работы приложения существуют методы протоколирования. Популярным инструментом для этих целей в мире
 +
Java является библиотека Log4j, которая разрабатывается в Apache Software Foundation. Текущую версию можно загрузить с [http://logging.apache.org/[http://logging.apache.org/].
 +
 +
Для использования библиотеки необходимо создать конфигурационный файл, который описывает, что, куда и как нужно протоколировать. Log4j имеет три базовые составляющие: logger, appender и layout.
 +
 +
layout — это элементы, определяющие вид и содержание записей. Изначально имеется несколько заранее созданных layout-ов, а в случае необходимости можно создать свой собственный.
 +
 +
Appender — это элемент, определяющий местоположение протокола, с его помощью задается тип протоколирования:
 +
* файловое протоколирование (FileAppender);
 +
* консольное протоколирование (ConsoleAppender);
 +
* протоколирование в базы данных (JDBCAppender);
 +
* протоколирование на SMTP-сервера (SMTPAppender) и др.
 +
 +
Logger — это элемент, который обеспечивает протоколирование какого-либо события. Если обратиться к ранее приведенной аналогии
 +
ленточного конвейера, logger — это и есть тот самый конвейер, вызывая методы которого, мы формируем протокол работы программы.
 +
Элемент logger предусматривает следующие уровни протоколирования: DEBUG, INFO, WARN, ERROR, FATAL; уровням соответствуют методы класса org.apache.log4j.Logger: debug; info; warn; error; fatal.
 +
 +
Ниже представлен пример записей для конфигурационного файла нашего приложения, которые нужно сохранить в файл с названием '''log.properties''' (название файла может быть любым).
 +
 +
<code>
 +
log4j.logger.simple=DEBUG, nameLogAppender
 +
log4j.appender.nameLogAppender=org.apache.log4j.FileAppender
 +
log4j.appender.nameLogAppender.File=nameLogFile.log
 +
log4j.appender.nameLogAppender.layout=org.apache.log4j.SimpleLayout
 +
</code>
 +
 +
Первая строка указывает используемый уровень logger (DEBUG) и appender (nameLogAppender). Далее идут настройки appender: указание типа — FileAppender. В третьей строке указываем путь до файла журнала, а в последней — формат записи. Использовать экземпляр класса Logger можно примерно так (пример протоколирования приведен на диске в каталоге '''examples 3'''):
 +
<source lang = "java">
 +
File propertiesFile = new File("log.properties");
 +
PropertyConfigurator.configure(propertiesFile.toString());
 +
Logger logger = Logger.getLogger("simple");
 +
logger.info("the program has started");
 +
</source>
  
 
=== Документы XML ===
 
=== Документы XML ===
 +
Проектируя и создавая ПО, невозможно не столкнутся с миром XML (Extensible Markup Language). XML был создан в недрах World Wide
 +
Web Consortium (W3C) для преодоления ограничений языка HTML. Можно сказать, что HTML — один из самых успешных языков, область
 +
его использования с каждым годом растет (в основном в объемах). Несмотря на это, почему же W3C создал XML, и зачем вам использовать этот язык? В чем ограниченность HTML? Ответ на эти вопросы один:XML был создан для обеспечения взаимодействия разнородных систем.
 +
 +
HTML, как и любой другой текстовый язык, не позволяет перенести смысл тех данных, которые он хранит. XML был разработан для решения этой задачи с прицелом на Web, но получился таким удачным, что его стали использовать практически везде. Суть XML в том, что он хранит семантический смысл данных, поэтому выполнив анализ такого XML-документа, система может «понять» полученные данные. В Интернете существует большое количество информации, посвященной XML (например, [http://www.ibm.com/developerworks/xml/[www.ibm.com/developerworks/xml/]]), поэтому не будем подробно останавливаться на его преимуществах, а сразу приступим к использованию.
 +
 +
<source lang = "xml">
 +
<?xml version="1.0" ?>
 +
<line name="firstLine">
 +
<point id="1" theX="1" theY="1"/>
 +
<point id="2" theX="2" theY="2"/>
 +
</line>
 +
</source>
 +
 +
Выше приведен простой пример XML-документа, содержащего корневой элемент line, который, в свою очередь, содержит два узла с
 +
тремя атрибутами (id, theX, theY) каждый.
  
 
=== Создание XML-документа ===
 
=== Создание XML-документа ===
 +
Говоря про создание XML-документа, я подразумеваю создание дерева XML-документа в памяти системы, то есть объекта DOM (Document Object Model). DOM была создана W3C, и это — официальная Рекомендация консорциума. В противовес DOM существует SAX (Simple API for XML). С моделью SAX можно ознакомиться более подробно в
 +
[http://www-128.ibm.com/developerworks/ru/views/xml/libraryview.jsp[http://www-128.ibm.com/developerworks/ru/views/xml/libraryview.jsp]. Основная разница между методами заключается в том, что DOM обеспечивает виртуальное представление XML-файла в памяти системы, в то время как SAX — это событийная модель обработки, в которой в момент встречи определенного элемента вызывается соответствующее событие.
 +
 +
Для создания «отображения» XML-файла в памяти системы необходимо воспользоваться классом Document, экземпляр которого можно получить следующим образом:
 +
<source lang = "java">
 +
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 +
DocumentBuilder db = dbf.newDocumentBuilder();
 +
Document doc = db.newDocument();
 +
</source>
 +
Создание корневого узла XML документа можно выполняется так:
 +
<source lang = "java">
 +
Element root = doc.createElement("line");
 +
root.setAttribute("name", "firstLine");
 +
</source>
 +
С помощью метода createElement(name) производится создание элементов, в то время как создание и установка значений атрибутов
 +
элементов производится с помощью метода setAttribute(name, value), где name — имя атрибута, а value — его значение. С помощью метода appendChild можно добавить узел в элемент или произвести запись элемента в документ XML, например, так:
 +
<source lang = "java">
 +
root.appendChild(item);
 +
doc.appendChild(root);
 +
</source>
 +
Более детально процесс создания XML документа на примере класса Line изложен в директории '''examples 4'''. При вызове метода
 +
createDoc() этого класса создается экземпляр класса Document, идентичный приведенному ранее коду XML.
  
 
=== Сохранение XML-документа ===
 
=== Сохранение XML-документа ===
 +
Для сохранения созданного в памяти документа предлагаю воспользоваться методом saveXML(docToSave, pathToFile, charSet). В процессе сохранения участвует уже знакомый нам OutputStreamWriter, а также экземпляр класса Transformer, который обеспечивает преобразование объекта DOMSource в выходной поток.
 +
<source lang = "java">
 +
public void saveXML(Document docToSave, String pathToFile, String charSet) {
 +
  try {
 +
    Writer target = new OutputStreamWriter(new FileOutputStream(pathToFile), charSet);
 +
    Source source = new DOMSource(docToSave);
 +
    StreamResult dest = new StreamResult(target);
 +
    Transformer t = TransformerFactory.newInstance().newTransformer();
 +
    t.setOutputProperty(OutputKeys.ENCODING, charSet);
 +
    t.setOutputProperty(OutputKeys.INDENT, "yes");
 +
    t.transform(source, dest); 
 +
    target.flush();
 +
    target.close();
 +
  }
 +
  catch (Exception ex) {
 +
    ex.printStackTrace();
 +
  }
 +
}
 +
</source>
 +
Реализация этого метода представлена на диске в директории '''examples 5'''.
  
 
=== Загрузка XML-документа ===
 
=== Загрузка XML-документа ===
 +
Подобно сохранению XLM-документа, загрузка также достаточно просто выполняется с помощью экземпляра класса FileInputStream. Используя DocumentBuilderFactory, сгенерируем объект класса Document следующим образом:
 +
<source lang = "java">
 +
FileInputStream fis = new FileInputStream(pathToFile);
 +
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(fis);
 +
</source>
 +
Фактически, создание документа происходит вследствие выполнения метода parse. Для детально рассмотрения процесса загрузки
 +
XML документа обратитесь к директории '''examples 6''', расположенной на диске.
  
 
=== Извлечение данных из XML-документа ===
 
=== Извлечение данных из XML-документа ===
 +
Итак, что же делать с Document? Работать, естественно! Прежде чем приступить к обработке XML-документа, хочу обратить внимание на одну неприятную особенность: почти всегда XML документ содержит пустые узлы или символы перевода каретки (неизбежное зло форматирования). К сожалению, такая особенность существенно затрудняет процесс обработки XML-документа. Поэтому следует всегда проводить нормализацию с помощью определенного в интерфейсе метода normalize(), или, если реализация Document не имеет такой возможности или нормализация выполняется некорректно — воспользуйтесь методом normalizeDocument класса FileXMLReader (вы найдете его все в той же директории '''examples 6'''). Вызвать данный метод можно следующим образом:
 +
<source lang = "java">
 +
normalizeDocument(doc.getDocumentElement());
 +
</source>
 +
Изложенный выше материал получился несколько другого формата, нежели предыдущие статьи. Статья содержит небольшие примеры
 +
и отсылает на набор сознательно упрощенных готовых решений, благодаря которым вы сможете работать с файлами, производить прото-
 +
колирование деятельности вашего приложения, начать работать с данными в формате XML. Цель приведенных примеров — обеспечить вас
 +
необходимым минимумом информации и дать направление для поиска ответов на Ваши вопросы. В [[LXF87/88:Java|следующей статье]] мы расмотрим потоки.

Текущая версия на 17:00, 27 апреля 2008

Содержание

[править] Хранение данных

ЧАСТЬ 3: Даже самой замечательной программе надо откуда-то черпать данные для своей работы. Данные, как известно, хранятся в файлах. Тему продолжает Антон Черноусов.

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

В течение третьего урока мы поговорим о работе с файлами, о протоколировании работы программы и коснемся методов работы с XML-данными.

[править] Файлы — потоки

Сказочное королевство под руководством царевны Несмеяны (так как cупруг практически всегда отсутствовал), благодаря талантам и приобретенным навыкам, стало разрастаться, и результаты полюдья просто-напросто перестали помещаться в семейный чулан. Чтобы накапливать и хранить богатства, потребовались дополнительные помещения, роль которых для нас привычно играют файлы.

Отношение к файлам в Java достаточно непростое: если рассматривать файл как устройство для ввода/вывода информации — с этой точ ки зрения он подобен блоку памяти или экрану, интерфейс доступа к которому унифицирован: это поток. Но несмотря на унифицированный интерфейс, существует большое количество классов сходной функциональности, в которых легко запутаться.

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

[править] Чтение данных

Разнообразие классов для работы с файлами позволяет выбрать для себя ту связку, которая больше нравится. Лично я для чтения данных использую BufferedReader, InputStreamReader и FileInputStream. Собственно взаимодействие этих классов для программиста заканчивается в момент создания экземпляра BufferedReader, что делается следующим образом:

BufferedReader br = null;
br = new BufferedReader(new InputStreamReader(new FileInputStream(pathToFile), encoding));

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

protected static String DEFAULT_ENCODING = "UTF-8";

Любой текстовый файл можно представить себе в виде набора строк, поэтому давайте реализуем метод для считывания содержимого файла в массив String[]. Далее представлен метод rippedCurrentFile(path ToFile, encoding) класса FileRipper, который извлекает данные из файла с помощью метода readLine() экземпляра класса BufferedReader:

protected boolean rippedCurrentFile(String pathToFile, String encoding) {
  // connecting to file
  BufferedReader br = null;
  try {
    br = new BufferedReader(new InputStreamReader(new FileInputStream(pathToFile),encoding));
  } 
  catch (UnsupportedEncodingException e) {
    this.error = FILE_ERROR_UNSUPPORTED_ENCODING; return false;
  } 
  catch (FileNotFoundException e) {
    this.error = FILE_ERROR_NO_FILE; return false;
  }
  // ripping the file
  String str = null;
  ArrayList allStrings = new ArrayList();
  try {
    while (!(str = br.readLine()).equals(null)) { allStrings.add(str); }
  } 
  catch (IOException e) {
    this.error = FILE_ERROR_IO_READ; return false;
  } 
  catch (NullPointerException e) {
    this.error = FILE_ERROR_END_OF_FILE;
  }
  // free the resources
  try {
    br.close();
  } 
  catch (IOException e) {
    this.error = FILE_ERROR_IO_CLOSE; return false;
  }
  this.allStrings = (String[]) allStrings.toArray(new String[0]); 
  return true;
}

Полный код примера, в том числе код класса ConsoleToFileRipper, применяющий экземпляр класса FileRipper для извлечения данных из файла, можно найти на диске в каталоге examples 1.

[править] Запись данных

Процесс записи данных в файл хоть и отличается от чтения, но тоже достаточно похож на организацию конвейера. Для записи я обычно использую связку BufferedWriter, OutputStreamWriter, FileOutputStream.

BufferedWriter out;
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(pathToFile), encoding));

Для освобождения ресурсов, которые используют экземпляры классов BufferedReader и BufferedWriter, необходимо вызвать метод close().

Далее приведу простой пример метода, который записывает строковый массив в файл (полный код метода расположен на диске в директории examples 2):

public boolean createCurrentFile(String pathToFile, String[] allStrings, String encoding){
  try {
    BufferedWriter out;
    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(pathToFile), encoding));
    for (int i = 0; i < allStrings.length; i++) {
      out.write(allStrings[i]); out.write('\n');
    }
    out.close();
  } 
  catch (IOException e) {
    e.printStackTrace(); 
    return false;
  }
  return true;
}

[править] Свободный доступ

В представленных ранее примерах доступ к данным осуществляется последовательно, что не всегда удобно (хотя в большинстве случаев именно такой доступ и используется). Для осуществления чтения и записи данных из файла в произвольном порядке существует специальный класс RandomAccessFile, экземпляр которого создается следующим образом:

RandomAccessFile raf = new RandomAccessFile(pathToFile, mode);

При этом pathToFile — путь до файла, а mode — режим работы. mode может принимать значения: r (только чтение), rw (чтение-запись), rws (чтение-запись с синхронным сохранением содержимого и метаданных), rwd (чтение-запись с синхронным сохранением содержимого файла). К сожалению, кодировку указать нельзя. Огромным преимуществом подхода является то, что с помощью метода getFilePointer() можно узнать текущее месторасположение указателя, а с помощью метода seek() можно передвинуть указатель в необходимое место в файле. Я предпочитаю не использовать данный класс — считайте это личным предубеждением.

[править] Протоколирование работы программы

Для контроля и анализа работы приложения существуют методы протоколирования. Популярным инструментом для этих целей в мире Java является библиотека Log4j, которая разрабатывается в Apache Software Foundation. Текущую версию можно загрузить с [http://logging.apache.org/.

Для использования библиотеки необходимо создать конфигурационный файл, который описывает, что, куда и как нужно протоколировать. Log4j имеет три базовые составляющие: logger, appender и layout.

layout — это элементы, определяющие вид и содержание записей. Изначально имеется несколько заранее созданных layout-ов, а в случае необходимости можно создать свой собственный.

Appender — это элемент, определяющий местоположение протокола, с его помощью задается тип протоколирования:

  • файловое протоколирование (FileAppender);
  • консольное протоколирование (ConsoleAppender);
  • протоколирование в базы данных (JDBCAppender);
  • протоколирование на SMTP-сервера (SMTPAppender) и др.

Logger — это элемент, который обеспечивает протоколирование какого-либо события. Если обратиться к ранее приведенной аналогии ленточного конвейера, logger — это и есть тот самый конвейер, вызывая методы которого, мы формируем протокол работы программы. Элемент logger предусматривает следующие уровни протоколирования: DEBUG, INFO, WARN, ERROR, FATAL; уровням соответствуют методы класса org.apache.log4j.Logger: debug; info; warn; error; fatal.

Ниже представлен пример записей для конфигурационного файла нашего приложения, которые нужно сохранить в файл с названием log.properties (название файла может быть любым).

log4j.logger.simple=DEBUG, nameLogAppender
log4j.appender.nameLogAppender=org.apache.log4j.FileAppender
log4j.appender.nameLogAppender.File=nameLogFile.log
log4j.appender.nameLogAppender.layout=org.apache.log4j.SimpleLayout

Первая строка указывает используемый уровень logger (DEBUG) и appender (nameLogAppender). Далее идут настройки appender: указание типа — FileAppender. В третьей строке указываем путь до файла журнала, а в последней — формат записи. Использовать экземпляр класса Logger можно примерно так (пример протоколирования приведен на диске в каталоге examples 3):

File propertiesFile = new File("log.properties");
PropertyConfigurator.configure(propertiesFile.toString());
Logger logger = Logger.getLogger("simple");
logger.info("the program has started");

[править] Документы XML

Проектируя и создавая ПО, невозможно не столкнутся с миром XML (Extensible Markup Language). XML был создан в недрах World Wide Web Consortium (W3C) для преодоления ограничений языка HTML. Можно сказать, что HTML — один из самых успешных языков, область его использования с каждым годом растет (в основном в объемах). Несмотря на это, почему же W3C создал XML, и зачем вам использовать этот язык? В чем ограниченность HTML? Ответ на эти вопросы один:XML был создан для обеспечения взаимодействия разнородных систем.

HTML, как и любой другой текстовый язык, не позволяет перенести смысл тех данных, которые он хранит. XML был разработан для решения этой задачи с прицелом на Web, но получился таким удачным, что его стали использовать практически везде. Суть XML в том, что он хранит семантический смысл данных, поэтому выполнив анализ такого XML-документа, система может «понять» полученные данные. В Интернете существует большое количество информации, посвященной XML (например, [www.ibm.com/developerworks/xml/]), поэтому не будем подробно останавливаться на его преимуществах, а сразу приступим к использованию.

<?xml version="1.0" ?>
<line name="firstLine">
<point id="1" theX="1" theY="1"/>
<point id="2" theX="2" theY="2"/>
</line>

Выше приведен простой пример XML-документа, содержащего корневой элемент line, который, в свою очередь, содержит два узла с тремя атрибутами (id, theX, theY) каждый.

[править] Создание XML-документа

Говоря про создание XML-документа, я подразумеваю создание дерева XML-документа в памяти системы, то есть объекта DOM (Document Object Model). DOM была создана W3C, и это — официальная Рекомендация консорциума. В противовес DOM существует SAX (Simple API for XML). С моделью SAX можно ознакомиться более подробно в [http://www-128.ibm.com/developerworks/ru/views/xml/libraryview.jsp. Основная разница между методами заключается в том, что DOM обеспечивает виртуальное представление XML-файла в памяти системы, в то время как SAX — это событийная модель обработки, в которой в момент встречи определенного элемента вызывается соответствующее событие.

Для создания «отображения» XML-файла в памяти системы необходимо воспользоваться классом Document, экземпляр которого можно получить следующим образом:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();

Создание корневого узла XML документа можно выполняется так:

Element root = doc.createElement("line");
root.setAttribute("name", "firstLine");

С помощью метода createElement(name) производится создание элементов, в то время как создание и установка значений атрибутов элементов производится с помощью метода setAttribute(name, value), где name — имя атрибута, а value — его значение. С помощью метода appendChild можно добавить узел в элемент или произвести запись элемента в документ XML, например, так:

root.appendChild(item);
doc.appendChild(root);

Более детально процесс создания XML документа на примере класса Line изложен в директории examples 4. При вызове метода createDoc() этого класса создается экземпляр класса Document, идентичный приведенному ранее коду XML.

[править] Сохранение XML-документа

Для сохранения созданного в памяти документа предлагаю воспользоваться методом saveXML(docToSave, pathToFile, charSet). В процессе сохранения участвует уже знакомый нам OutputStreamWriter, а также экземпляр класса Transformer, который обеспечивает преобразование объекта DOMSource в выходной поток.

public void saveXML(Document docToSave, String pathToFile, String charSet) {
  try {
    Writer target = new OutputStreamWriter(new FileOutputStream(pathToFile), charSet);
    Source source = new DOMSource(docToSave);
    StreamResult dest = new StreamResult(target);
    Transformer t = TransformerFactory.newInstance().newTransformer();
    t.setOutputProperty(OutputKeys.ENCODING, charSet);
    t.setOutputProperty(OutputKeys.INDENT, "yes");
    t.transform(source, dest);  
    target.flush();
    target.close();
  } 
  catch (Exception ex) {
    ex.printStackTrace();
  }
}

Реализация этого метода представлена на диске в директории examples 5.

[править] Загрузка XML-документа

Подобно сохранению XLM-документа, загрузка также достаточно просто выполняется с помощью экземпляра класса FileInputStream. Используя DocumentBuilderFactory, сгенерируем объект класса Document следующим образом:

FileInputStream fis = new FileInputStream(pathToFile);
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(fis);

Фактически, создание документа происходит вследствие выполнения метода parse. Для детально рассмотрения процесса загрузки XML документа обратитесь к директории examples 6, расположенной на диске.

[править] Извлечение данных из XML-документа

Итак, что же делать с Document? Работать, естественно! Прежде чем приступить к обработке XML-документа, хочу обратить внимание на одну неприятную особенность: почти всегда XML документ содержит пустые узлы или символы перевода каретки (неизбежное зло форматирования). К сожалению, такая особенность существенно затрудняет процесс обработки XML-документа. Поэтому следует всегда проводить нормализацию с помощью определенного в интерфейсе метода normalize(), или, если реализация Document не имеет такой возможности или нормализация выполняется некорректно — воспользуйтесь методом normalizeDocument класса FileXMLReader (вы найдете его все в той же директории examples 6). Вызвать данный метод можно следующим образом:

normalizeDocument(doc.getDocumentElement());

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

Персональные инструменты
купить
подписаться
Яндекс.Метрика