LXF86:Java
(→Файлы — потоки) |
Lockal (обсуждение | вклад) м (восстановление кавычек в коде 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
|
|
|
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
Содержание |
[править] Хранение данных
ЧАСТЬ 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. Цель приведенных примеров — обеспечить вас необходимым минимумом информации и дать направление для поиска ответов на Ваши вопросы. В следующей статье мы расмотрим потоки.