特殊文件

一、属性文件
1.1 特殊文件概述
前面我们学习了 IO 流,我们知道 IO 流是用来读、写文件中的数据。但是我们接触到的文件都是普通的文本文件,普通的文本文件里面的数据是没有任何格式规范的,用户可以随意编写,如下图所示。
像这种普通的文本文件,没有任何规律可言,不方便程序对文件中的数据信息处理。

在以后的 Java 开发过程中还会遇到一些特殊的文本文件,这些文件是有一些格式要求的,方便程序对文件中的数据进行处理。
比如,后面我们会用到两种特殊的文本文件,一种是 properties 文件,还有一种是 xml 文件。如下图所示。

- 后缀为.properties 的文件,称之为属性文件,它可以很方便的存储一些类似于键值对的数据。经常当做软件的配置文件使用。
- 而 xml 文件能够表示更加复杂的数据关系,比如要表示多个用户的用户名、密码、家乡、性别等。在后面,也经常当做软件的配置文件使用。
现在,学习特殊的文件主要学习什么呢? 主要学习以下的三点

1.2 Properties 属性文件
接下来,我们先学习 Properties 这种属性文件。首先我们要掌握属性文件的格式:
- 属性文件后缀以
.properties结尾 - 属性文件里面的每一行都是一个键值对,键和值中间用=隔开。比如:
admin=123456 #表示这样是注释信息,是用来解释这一行配置是什么意思。- 每一行末尾不要习惯性加分号,以及空格等字符;不然会把分号,空格会当做值的一部分。
- 键不能重复,值可以重复
如下图所示

接下来,我们学习如何读取属性文件中的数据。这里需要给同学们,介绍一个来叫 Properties.
1.Properties是什么?
Properties是Map接口下面的一个实现类,所以Properties也是一种双列集合,用来存储键值对。
但是一般不会把它当做集合来使用。
2.Properties核心作用?
Properties类的对象,用来表示属性文件,可以用来读取属性文件中的键值对。
- 使用 Properties 读取属性文件中的键值对,需要用到的方法如下。

实用 Properties 读取属性文件的步骤如下
1、创建一个Properties的对象出来(键值对集合,空容器)
2、调用load(字符输入流/字节输入流)方法,开始加载属性文件中的键值对数据到properties对象中去
3、调用getProperty(键)方法,根据键取值
代码如下:
package com.itheima.d1_properties;
import java.util.Properties;
import java.util.Set;
/*
目标:掌握使用Properties类读取属性文件中的键值对信息。
Properties是一个Map集合(键值对集合),但是我们一般不会当集合使用。
核心作用:Properties是用来代表属性文件的,通过Properties可以读写属性文件里的内容。
构造方法
public Properties() 用于构建Properties集合对象(空容器)
成员方法
public String getProperty(String key) 根据键获取值(其实就是get方法的效果)
public Set<String> stringPropertyNames() 获取全部键的集合(其实就是ketSet方法的效果)
public Object setProperty(String key, String value) 保存键值对数据到Properties对象中去。put
*/
public class PropertiesTest1 {
public static void main(String[] args) {
/*
1 Properties双列集合没有泛型
2 使用Properties集合以后键和值存储的都是String
*/
Properties properties = new Properties();
// public Object setProperty(String key, String value) 保存键值对数据到Properties对象中去
properties.setProperty("zhangsan", "123");
properties.setProperty("lisi", "345");
properties.setProperty("wangwu", "456");
// public Set<String> stringPropertyNames() 获取全部键的集合(其实就是ketSet方法的效果)
Set<String> set = properties.stringPropertyNames();
for (String key : set) {
// public String getProperty(String key) 根据键获取值(其实就是get方法的效果)
String value = properties.getProperty(key);
System.out.println(key + "---" + value);
}
// properties.put("zhangsan" , "123");
// properties.put("lisi" , "345");
// properties.put("wangwu" , "456");
//
// // System.out.println(properties);
// // 键找值
// Set<Object> set = properties.keySet();
// for (Object key : set) {
// Object value = properties.get(key);
// System.out.println(key + "---" + value);
// }
}
}
- 使用 Properties 往属性文件中写键值对,需要用到的方法如下

往 Properties 属性文件中写键值对的步骤如下
1、先准备一个.properties属性文件,按照格式写几个键值对
1、创建Properties对象出来,
2、调用setProperty存储一些键值对数据
3、调用store(字符输出流/字节输出流, 注释),将Properties集合中的键和值写到文件中
注意:第二个参数是注释,必须得加;
先准备一个 users.properties 属性文件,如下图所示

接下来,编写代码读取上面的属性文件。代码如下:
package com.itheima.d1_properties;
import java.io.*;
import java.util.Properties;
/*
目标:掌握把键值对数据存入到属性文件中去
public void store(OutputStream os, String comments) 把键值对数据,通过字节输出流写出到属性文件里去
public void store(Writer w, String comments) 把键值对数据,通过字符输出流写出到属性文件里去
public void load(InputStream is) 通过字节输入流,读取属性文件里的键值对数据
public void load(Reader reader) 通过字符输入流,读取属性文件里的键值对数据
需求 :
1 把键值对数据 柳岩=123 李沁=456 杨幂=789 保存到一个properties文件中
2 把文件中的三个键值对数据读到Properties集合中
*/
public class PropertiesTest2 {
public static void main(String[] args) throws Exception {
method1();
// method2();
}
private static void method2() throws IOException {
Properties properties = new Properties();
// public Object setProperty(String key, String value) 保存键值对数据到Properties对象中去
properties.setProperty("zhangsan", "123");
properties.setProperty("lisi", "345");
properties.setProperty("wangwu", "456");
// public void store(OutputStream os, String comments) 把键值对数据,通过字节输出流写出到属性文件里去
// public void store(Writer w, String comments) 把键值对数据,通过字符输出流写出到属性文件里去
properties.store(new FileWriter("user.properties") , null);
}
private static void method1() throws IOException {
// 创建双列集合
Properties properties = new Properties();
// public void load(InputStream is) 通过字节输入流,读取属性文件里的键值对数据
// public void load(Reader reader) 通过字符输入流,读取属性文件里的键值对数据
// 把文件中的键值对数据加载到双列集合中
properties.load(new FileReader("user.properties"));
System.out.println(properties);
// HashMap<String , String> hm = new HashMap<>();
//
// BufferedReader br = new BufferedReader(new FileReader("user.properties"));
//
// String line; // "zhangsan=123"
// while((line = br.readLine()) != null){
// String[] strings = line.split("=");// {"zhangsan" , "123"}
// hm.put(strings[0] ,strings[1]);
// }
//
// System.out.println(hm);
//
// br.close();
}
}
运行上面的代码,user2.properties 配置文件打开效果如下图所示。

二、XML 文件
2.1 XML 文件概述
XML 是可扩展的标记语言,意思是它是由一些标签组成 的,而这些标签是自己定义的。本质上一种数据格式,可以用来表示复杂的数据关系。
[!NOTE]
XML 文件有如下的特点:
- XML 中的
<标签名>称为一个标签或者一个元素,一般是成对出现的。- XML 中的标签名可以自己定义(可扩展),但是必须要正确的嵌套
- XML 中只能有一个根标签。
- XML 标准中可以有属性
- XML 必须第一行有一个文档声明,格式是固定的
<?xml version="1.0" encoding="UTF-8"?>- XML 文件必须是以.xml 为后缀结尾
如下图所示

接下,同学们可以跟着步骤新建一个 XML 文件,试试!

<?xml version="1.0" encoding="UTF-8" ?>
<!-- 注释:以上抬头声明必须放在第一行,必须有 -->
<!-- 根标签只能有一个 -->
<users>
<user id="1" desc="第一个用户">
<name>张无忌</name>
<sex>男</sex>
<地址>光明顶</地址>
<password>minmin</password>
</user>
<people>很多人</people>
<user id="2">
<name>敏敏</name>
<sex>女</sex>
<地址>光明顶</地址>
<password>wuji</password>
</user>
</users>
上面 XML 文件中的数据格式是最为常见的,标签有属性、文本、还有合理的嵌套。XML 文件中除了写以上的数据格式之外,还有一些特殊的字符不能直接写。
-
像
<,>,&等这些符号不能出现在标签的文本中,因为标签格式本身就有 <>,会和标签格式冲突。如果标签文本中有这些特殊字符,需要用一些占位符代替。
< 表示 < > 表示 > & 表示 & ' 表示 ' " 表示 "<data> 3 < 2 && 5 > 4 </data> -
如果在标签文本中,出现大量的特殊字符,不想使用特殊字符,此时可以用 CDATA 区,格式如下
<data1> <![CDATA[ 3 < 2 && 5 > 4 ]]> </data1>
最后,给同学们聊聊,XML 在实际开发中有什么作用?

2.2 XML 解析 1
使用程序读取 XML 文件中的数据,称之为 XML 解析。这里并不需要我们自己写 IO 流代码去读取 xml 文件中的数据。其实有很多开源的,好用的 XML 解析框架,最知名的是 DOM4J(第三方开发的)

由于 DOM4J 是第三方提供的,所以需要把第三方提供的 Jar 包导入到自己的项目中来,才可以使用。具体步骤如下:

DOM4J 解析 XML 文件的思想是:文档对象模型(意思是把整个 XML 文档、每一个标签、每一个属性都等都当做对象来看待)。Dowument 对象表示真个 XML 文档、Element 对象表示标签(元素)、Attribute 对象表示属性、标签中的内容就是文本

DOM4J 解析 XML 需要用到的方法如下图所示

XML 解析的过程,是从根元素开始,从外层往里层解析。 我们先把 Document 对象,和根元素获取出来
public class Dom4JTest1 {
public static void main(String[] args) throws Exception {
// 1、创建一个Dom4J框架提供的解析器对象
SAXReader saxReader = new SAXReader();
// 2、使用saxReader对象把需要解析的XML文件读成一个Document对象。
Document document =
saxReader.read("properties-xml-log-app\\src\\helloworld.xml");
// 3、从文档对象中解析XML文件的全部数据了
Element root = document.getRootElement();
System.out.println(root.getName());
}
}

2.3 XML 解析 2
获取到 XML 文件的根元素之后,接下来,就可以用根元素在获取到它里面的子元素(包括子标签、表属性等)。需要用到的方法如下图所示

接下来,把上面的方法先一个一个的演示一下。
package com.itheima.d2_xml;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
/*
目标:掌握使用Dom4j框架解析XML文件。
流程说明:
1. 创建SAXReader对象,用于读取XML文件
2. 加载XML文件,获取Document对象(代表整个XML文档)
3. 获取根元素(XML中的最外层标签)
4. 从根元素中获取所有子元素
5. 遍历子元素,解析其中的属性值和文本内容
*/
public class Dom4JTest1 {
public static void main(String[] args) throws DocumentException {
// 1. 创建SAXReader解析器对象(Dom4j的核心读取工具)
SAXReader saxReader = new SAXReader();
// 2. 读取XML文件,生成Document对象
// ./xml_file/data.xml 表示项目根目录下的xml_file文件夹中的data.xml文件
Document document = saxReader.read(new File("./xml_file/data.xml"));
// 3. 获取XML文档的根元素(例如:<users> 标签)
Element rootElement = document.getRootElement();
// 4. 获取根元素下的所有直接子元素(例如:所有 <user> 标签)
List<Element> list = rootElement.elements();
// 5. 遍历每个子元素(每个user标签)
for (Element userElement : list) {
// 5.1 获取user元素的id属性
Attribute id = userElement.attribute("id");
// 5.2 获取id属性的值
String idValue = id.getValue();
System.out.println("id = " + idValue);
// 5.3 获取user元素下的username子元素,并获取其文本内容
Element username = userElement.element("username");
String usernameText = username.getText();
// 5.4 获取user元素下的password子元素,并获取其文本内容
Element password = userElement.element("password");
String passwordText = password.getText();
// 5.5 输出解析到的数据
System.out.println("username = " + usernameText);
System.out.println("password = " + passwordText);
System.out.println("=======================");
}
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<users>
<user id="1">
<username>ZhangSan</username>
<password>123456</password>
</user>
<user id="2">
<username>LiSi</username>
<password>567890</password>
</user>
</users>
id = 1
username = ZhangSan
password = 123456
=======================
id = 2
username = LiSi
password = 567890
=======================
进程已结束,退出代码为 0
public class Dom4JTest1 {
public static void main(String[] args) throws Exception {
// 1、创建一个Dom4J框架提供的解析器对象
SAXReader saxReader = new SAXReader();
// 2、使用saxReader对象把需要解析的XML文件读成一个Document对象。
Document document =
saxReader.read("properties-xml-log-app\\src\\helloworld.xml");
// 3、从文档对象中解析XML文件的全部数据了
Element root = document.getRootElement();
System.out.println(root.getName());
// 4、获取根元素下的全部一级子元素。
// List<Element> elements = root.elements();
List<Element> elements = root.elements("user");
for (Element element : elements) {
System.out.println(element.getName());
}
// 5、获取当前元素下的某个子元素。
Element people = root.element("people");
System.out.println(people.getText());
// 如果下面有很多子元素user,默认获取第一个。
Element user = root.element("user");
System.out.println(user.elementText("name"));
// 6、获取元素的属性信息呢?
System.out.println(user.attributeValue("id"));
Attribute id = user.attribute("id");
System.out.println(id.getName());
System.out.println(id.getValue());
List<Attribute> attributes = user.attributes();
for (Attribute attribute : attributes) {
System.out.println(attribute.getName() + "=" + attribute.getValue());
}
// 7、如何获取全部的文本内容:获取当前元素下的子元素文本值
System.out.println(user.elementText("name"));
System.out.println(user.elementText("地址"));
System.out.println(user.elementTextTrim("地址")); // 取出文本去除前后空格
System.out.println(user.elementText("password"));
Element data = user.element("data");
System.out.println(data.getText());
System.out.println(data.getTextTrim()); // 取出文本去除前后空格
}
}
2.4 XML 文件写入
在前面我们已经学习了 XML 解析,意思就是使用程序把 XML 文件中的数据读取出来。反过来能不能往 XML 文件中写入数据呢? 答案是可以的。
DOM4J 也提供了往 XML 文件中写标签的方法,但是用起来比较麻烦。这里不建议使用
我们自己使用 StringBuilder 按照标签的格式拼接,然后再使用 BufferedWriter 写到 XML 文件中去就可以了。
public class Dom4JTest2 {
public static void main(String[] args) {
// 1、使用一个StringBuilder对象来拼接XML格式的数据。
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n");
sb.append("<book>\r\n");
sb.append("\t<name>").append("从入门到跑路").append("</name>\r\n");
sb.append("\t<author>").append("dlei").append("</author>\r\n");
sb.append("\t<price>").append(999.99).append("</price>\r\n");
sb.append("</book>");
try (
BufferedWriter bw = new BufferedWriter(new FileWriter("properties-xml-log-app/src/book.xml"));
){
bw.write(sb.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.5 XML 约束(了解)
各位小伙伴,关于 XML 还有最后一个知识需要大家了解一下。这个知识叫做约束 XML 文件的编写,我讲这个知识的目的是因为同学们以后在开发过程中会遇到这个知识,但是这个代码不需要大家写,了解一下就可以了。
首先,说一些什么是 XML 约束?
XML 约束指的是限制 XML 文件中的标签或者属性,只能按照规定的格式写。
比如我在项目中,想约束一个 XML 文件中的标签只能写 < 书 >、< 书名 >、< 作者 >、< 售价 > 这几个标签,如果写其他标签就报错。

怎么才能达到上面的效果呢?有两种约束技术,一种是 DTD 约束、一种是 Schame 约束。
-
DTD 约束案例
如下图所示 book.xml 中引入了 DTD 约束文件,book.xml 文件中的标签就受到 DTD 文件的约束

DTD 文件解释
<!ELEMENT 书架(书+)> 表示根标签是<书架>,并且书架中有子标签<书> <!ELEMENT 书(书名、作者、售价)> 表示书是一个标签,且书中有子标签<书名>、<作者>、<售价> <!ELEMENT 书名(#PCDATA)> 表示<书名>是一个标签,且<书名>里面是普通文本 <!ELEMENT 作者(#PCDATA)> 表示<作者>是一个标签,且<作者>里面是普通文本 <!ELEMENT 售价(#PCDATA)> 表示<售价>是一个标签,且<售价>里面是普通文本 -
Schame 约束案例
如下图所示,左边的 book2.xml 文件就受到右边 schame 文件(.xsd 结尾的文件)的约束。

三、习题
题目 1
请编写程序,定义一个 Properties 对象,存储以下内容:
Properties pro = new Properties();
pro.setProperty("brand", "Iphone11Pro Max");
pro.setProperty("capacity", "256G");
pro.setProperty("price", "10899");
请将上述内容写入到文件 iphone.properties 中(自己选定文件存储的位置)。
答案:
package com.itheima.E1_properties;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class test {
public static void main(String[] args) throws IOException {
write();
}
public static void write() throws IOException {
Properties pro = new Properties();
pro.setProperty("brand", "iPhone11Pro Max");
pro.setProperty("capacity", "256G");
pro.setProperty("price", "10899");
pro.store(new FileWriter("iphone.properties"),null);
}
}

题目 2
将题目一中 iphone.properties 文件的数据加载到 Properties 对中,并遍历打印出来。
答案:
package com.itheima.E1_properties;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class Test02 {
public static void main(String[] args) throws IOException {
read();
}
public static void read() throws IOException {
Properties pro = new Properties();
pro.load(new FileReader("iphone.properties"));
System.out.println(pro);
}
}
{price=10899, brand=iPhone11Pro Max, capacity=256G}
进程已结束,退出代码为 0
题目 3
请定义 xml 文件,存储以下“图书”信息:
书名:《架构师成长之路》,出版社:黑马程序员,出版日期:2010-6-6,价格:125.00
书名:《JavaEE企业级开发》,出版社:黑马研究院,出版日期:2011-5-5,价格:115.80
书名:《Oracle指南》,出版社:清华计算机,出版日期:2012-7-7,价格:89.00
要求:
- 使用:文档声明
- “标签名”不可使用中文。
- 文件名为:books.xml
- 根元素为:<books>
答案:
<?xml version="1.0" encoding="utf-8" ?>
<books>
<book id = "1">
<bookname>《架构师成长之路》</bookname>
<publisher>黑马程序员</publisher>
<publishdate>2010-6-6</publishdate>
<price>125.00</price>
</book>
<book id = "2">
<bookname>《JavaEE企业级开发》</bookname>
<publisher>黑马研究院</publisher>
<publishdate>2011-5-5</publishdate>
<price>115.00</price>
</book>
<book id = "3">
<bookname>《Oracle指南》</bookname>
<publisher>清华大学</publisher>
<publishdate>2012-7-7</publishdate>
<price>89.00</price>
</book>
</books>
题目 4
- 请将 dom4j 包导入到项目中。
- 使用 dom4j 读取上一题中的 books.xml 文件,解析每条数据,并打印
package com.itheima.E2_xml;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.List;
public class Test02 {
public static void main(String[] args) throws DocumentException {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read("./xml_file/books.xml");
Element rootElement = document.getRootElement();
List<Element> list = rootElement.elements();
for (Element bookelement : list) {
Attribute id = bookelement.attribute("id");
String idValue = id.getValue();
System.out.println("id:" + idValue);
Element bookname = bookelement.element("bookname");
String booknameText = bookname.getText();
Element publisher = bookelement.element("publisher");
String publisherText = publisher.getText();
Element publishdate = bookelement.element("publishdate");
String publishdateText = publishdate.getText();
Element price = bookelement.element("price");
String priceText = price.getText();
System.out.println("书名:" + booknameText);
System.out.println("出版社:" + publisherText);
System.out.println("出版日期:" + publishdateText);
System.out.println("价格:" + priceText);
System.out.println("-------------------------------------");
}
}
}
id:1
书名:《架构师成长之路》
出版社:黑马程序员
出版日期:2010-6-6
价格:125.00
-------------------------------------
id:2
书名:《JavaEE企业级开发》
出版社:黑马研究院
出版日期:2011-5-5
价格:115.00
-------------------------------------
id:3
书名:《Oracle指南》
出版社:清华大学
出版日期:2012-7-7
价格:89.00
-------------------------------------
进程已结束,退出代码为 0
