学习目标:掌握字节输出流FileWriter和字节输入流FileReader的使用方法。
FileWriter类
前面介绍了字节输入流FileInputStream和字节输出流FileOutputStream。本节介绍字节输出流FileWriter的使用方法。
编程案例
在java编程中,经常使用配置文件存储程序的一些配置属性,如数据库连接地址、访问数据库的账户和密码、下载文件的存储路径等配置属性。
本次编程任务要求创建一个配置文件,该配置文件存储三个属性,分别是数据库连接地址、访问数据库的账户以及访问数据库的密码。程序启动后,要求用户输入数据库连接地址、访问数据库的账户以及访问数据库的密码,并将用户输入的值存储到配置文件中。
配置文件结构如下:
jdbc.url=jdbc:mysql://192.168.50.25:3306
jdbc.username=root
jdbc.password=123456
编程探讨
编程案例给出的任务要求是创建并输出一个存储数据库连接属性的配置文件,其给出的结构是文本文件,只有三行内容,第一行内容存储数据库的连接地址,第二行内容存储数据库访问账号,第三行内容存储数据库访问密码。
配置文件每行等号的左侧是配置属性名,如jdbc.url、jdbc.username、jdbc.password,等号右侧是配置属性的值,如
jdbc:mysql://192.168.50.25:3306、root、123456。
考虑到配置文件是文本文件,可以使用FileWriter类来输出文件。FileWriter类是字符输出类,它的构造方法同FileOutputStream相同,也提供了四个常用的构造方法。分别说明如下:
场景1:应用File对象,实例化一个FileWriter对象
FileWriter(File file);
场景2:应用给出文件路径,实例化一个FileWriter对象
FileWriter(String fileName);
场景3:写入数据时,如果文件已存在,需要在实例化FileWriter对象时,指明写入的数据是覆盖原文件,还是将写入的数据追加到文件尾部。
FileWriter(File file, boolean append);
FileWriter(String fileName, boolean append);
append为true时将写入的数据追加到文件尾部,为false时覆盖原文件。
本案例为新建配置文件,采用FileWriter(File file)构造方法,通过File对象创建一个新文件。
FileWriter对象实例化后,可以使用FileWriter提供的写入方法,将配置内容写入到配置文件。FileWriter类提供了三种写入方法,分别说明如下:
● public void write(int c) throws IOException
该方法写入单个字符c到文件,当需要单字符写入文件时,可以采用该方法。例如,当需要排除某些字符写入文件时、当需要判断每个写入的字符值时。
● public void write(char [] c, int offset, int len)
该方法将字符数组c的部分或全部内容写入文件,offset为数组的起始偏移量,len为写入的数组长度,当需要将数组的某一部分写入文件时,可以采用该方法。例如,存储一段文字内容的数组,可能只需要写入后半部内容。
● public void write(String s, int offset, int len)
该方法将字符串对象s的部分或全部内容写入文件,offset为字符串对象内容的起始偏移量,len为写入的字符串内容长度。当需要将字符串对象的内容全部或部分写入文件时,可以采用该方法。
● public void write(String s)
该方法将字符串对象写入文件,当需要将字符串对象全部内容写入文件时,可采用此方法。例如,本案例中数据库的连接地址、数据库访问账号、数据库访问密码均需要从用户处获取输入,并存储到字符串对象中。因此,本案例可以采用该方法将字符串内容写入到配置文件。
编程实现
程序声明三个字符串对象,分别存储用户输入的数据库连接地址、数据库访问账号、数据库访问密码。实例化File对象创建配置文件,实例化FileWriter对象打开字符输出流,调用FileWriter类write方法将程序声明的三个字符串对象按照配置文件结构要求格式化后写入到文件。
案例13:编程案例的代码实现。
在PUnit11项目新建filewriter包,在filewriter包下新建FileWriteDemo类。代码如下:
package filewriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
public class FileWriteDemo {
public static void main(String[] args) {
// url存储数据库的连接地址
String url = "";
// username存储数据库的访问账户
String username = "";
// password存储数据库的访问密码
String password = "";
// 声明Scanner变量,并用new运算符实例化Scanner
Scanner sc = new Scanner(System.in);
// 请求用户输入数据库连接地址、访问账户、访问密码
// 提示用户输入数据库连接地址
System.out.println("请输入数据库连接地址");
url = "jdbc.url=" + sc.next();
// 提示用户输入数据库访问账户
System.out.println("请输入数据库访问账户");
username = "jdbc.username=" + sc.next();
// 提示用户输入数据库访问密码
System.out.println("请输入数据库登录密码");
password = "jdbc.password=" + sc.next();
// 实例化File对象,文件路径为d://myproperty.txt
File file = new File("d://myproperty.txt");
try {
// 创建配置文件
file.createNewFile();
// 实例化FileWriter
FileWriter fos = new FileWriter(file);
// 写入数据库连接地址
fos.write(url);
fos.write("\n");
// 写入数据库访问账户
fos.write(username);
fos.write("\n");
// 写入数据库访问密码
fos.write(password);
fos.flush();
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
配置文件的属性内容由配置属性前缀+用户输入的内容拼接而成。例如:数据库连接地址由属性前缀“jdbc.url=” 和用户输入的值确定。最后,调用FileWriter的write写入配置文件的属性内容,并在每个属性内容后写入换行符。
程序执行结果如下图所示:

myproperty.txt文件内容如下图所示:

FileReader类
Java程序创建配置文件的目的是便于程序的修改和维护,程序需要的全局数据都可以存储到配置文件中,程序运行后可以直接读取配置文件获取全局数据,当需要修改全局数据时,只需要修改配置文件就可以了,而无需修改代码。
前面我们使用FileWriter创建了一个数据库配置文件,该配置文件主要用于存储数据库连接地址、数据库访问账号和密码。当程序需要连接数据库时,可以从配置文件中读取数据库的连接地址、访问账号和密码。这样做的好处是可以确保连接数据库所需要的数据是唯一的,如果数据改变了,仅需修改配置文件即可,所有涉及到连接数据库的代码都无需修改。
编程案例
编程任务的要求是读取前面创建的配置文件myproperty.txt,该配置文件结构如下:
jdbc.url=jdbc:mysql://192.168.50.25:3306
jdbc.username=root
jdbc.password=123456
在上面的文件结构中,jdbc.url、jdbc.username、jdbc.password为配置文件的属性名称,其等号右边的内容为属性的值。要求案例程序不仅要读出配置文件内容,还要识别出属性和属性值。
编程探讨
依据编程案例给出的任务要求,首先要读取配置文件内容,并识别出配置文件内容的属性和属性值。
读取配置文件内容,可以考虑使用FileReader类,FileReader类是字符输入类,它的构造方法同FileInputStream相同,也提供了四个常用的构造方法。分别说明如下:
场景1:应用File对象,实例化一个FileReader对象
FileReader (File file);
场景2:应用给出的文件路径,实例化一个FileReader对象
FileReader (String fileName);
本案例为读取配置文件,采用FileReader (String fileName)构造方法,直接传入配置文件路径。
FileReader对象实例化后,可以使用FileReader提供的读取方法,读取配置文件内容。FileReader类提供了两种读取方法,说明如下:
● public int read() throws IOException
该方法用于读取单个字符,返回int类型,该int类型表示字符。
● public int read(char [] c, int offset, int len) throws IOException
该方法用于从文件的offset位置开始,读取len个字符到数组c,返回读取的字符数。
FileReader并没有提供按行读取文本内容的方法,因此我们需要实现一个按行读取文本内容的函数,该函数从文件中循环读取单个字符,并赋值给String对象,当读取到换行符时,结束读取,返回String对象。
另外,程序还需要一个识别属性和值的函数,该函数识别传入的字符串对象,调用String类的Split方法,用‘=’号做分割符,识别出配置文件的属性和值。识别出的属性和值存储到HashMap集合中,其属性为HashMap的key,值为HashMap的value。
编程实现
程序实现涉及到类有FileReader类、File类、String类和HashMap集合类。其实现过程如下图所示:

上面所示的流程图中,按行读取和识别属性和值框分别为程序要实现的函数。
案例14:编程案例的代码实现。
在filewriter包下新建FileReadDemo类。代码如下:
package filewriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
public class FileReadDemo {
public static void main(String[] args) {
FileReadDemo demo = new FileReadDemo();
// 应用HashMap存储从配置文件读取的属性和值
HashMap<String, String> map = new HashMap<String, String>();
// 实例化FileReader对象
FileReader read;
String strLine = "";
boolean bHave = true;
try {
read = new FileReader("d://myproperty.txt");
do {
// 按行读取
strLine = demo.readLine(read);
// 识别属性和值,并将属性和值存储到map
demo.split(map, strLine);
bHave = !strLine.equals("error");
} while (bHave);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 遍历HashMap
Set<String> set = map.keySet();
for (Iterator<String> iterator = set.iterator(); iterator.hasNext(); ) {
String key = iterator.next();
String value = map.get(key);
System.out.println(key + "=" + value);
}
}
// 按行读取配置文件
public String readLine(FileReader reader) throws IOException {
String strLine = "";
int c;
while ((c = reader.read()) != -1) {
if (c == '\n' || c == '\r') {
return strLine;
} else {
strLine += (char) c;
}
}
if (c == -1 && strLine.isEmpty()) {
strLine = "error";
}
return strLine;
}
// 识别属性和值
public void split(HashMap<String, String> map, String str) {
String[] temp = str.split("=");
if (temp.length >= 2) {
map.put(temp[0], temp[1]);
}
}
}
FileReadDemo类的readLine()方法按行读取配置文件的内容,方法循环读取文件中的单个字符,当字符为\r或\n时,程序判断遇到了行结束符,返回已读取的字符串。
split函数用于识别配置文件的属性和值,并将属性和值加入到map集合中,因为配置文件中的属性和值用等号分割,因此应用String类的split方法将字符串用等号分割为包含两个元素的字符串数组,数组的第一个元素为属性,第二个元素为值。
程序最后遍历map集合,输出配置文件的属性和值。
程序执行结果如下图所示:

FileReader主要是读取文本类文件,可以读取单个字符,也可以将多个字符读取到一个字符数组中。遗憾的是,FileReader没有提供按行读取的方法,如果需要按行读取,需要自己实现按行读取方法。