从文件读取数据并生成对象
这里我们展示数据与代码分离的编程方式。
- 数据存储在文件中;
- 程序运行时,通过从文件中读取数据,赋值给程序中要用的对象;
- 以下例子中,我们用cats.txt存储3只猫猫的数据(name, breed, age):
- 每一行对应一只猫猫的三个属性,用
','
分隔; - 程序运行时,读取该文件中的每一行,实例化为3只猫猫。
- 每一行对应一只猫猫的三个属性,用
1. 使用Java自带的文件读取功能从文件中读取数据
1.1 准备数据文件目录
- 右键单击工程名,在工程的根目录创建一个文件夹folder:
data
。可以将程序需要用到的数据文件放在该文件夹下。 - 检查工程目录,看data文件夹是否正确创建,并且和src文件夹同级。
- 同时其eclipse中的目录结构如下:
1.2 准备数据文件
- 右键单击data文件夹,在该文件夹下创建数据文件cats.txt
- 打开cats.txt,填写如下数据:
# name, breed, age
# '#'开头的定义为注释行,编程的时候检查,如果一行是#开头,跳过
# 同时,可以有空行,编程时检查一行是否为空,若为空跳过
Milo, Main coon cat, 5
Leo, Devon Rex, 1
Charlie, Siamese, 3
说明:这里我们自己定义了一个数据的结构:
每一行包含一只猫的三个属性,用
','
分隔,逗号后可以有空格; 第一个属性是name
,第二个属性是breed
,第三个属性是age
; 如果一行是'#'
开头,表明是注释行,解析文件时会直接跳过; 可以有空行。
1.3 创建Cat类
注意检查Class所在的package是否正确.
Cat.java
package javaio;
public class Cat {
public String name;
public String breed;
public int age;
@Override
public String toString() {
/*
* 重写Cat类的toString方法,
* 直接打印一个Cat的对象时,将调用该方法获得需要打印的字符串
*/
return String.format("Cat: %s; Breed: %s; Age: %d", name, breed, age);
}
}
1.4 解析文件,并生成Cat的对象
JavaFileIO.java
package javaio;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class JavaFileIO {
public static void main(String[] args) {
/*
* 观察该project, 会发现data和src是平级目录 这里用的相对路径,指project下data文件夹下cats.txt文件
*/
List<Cat> myCats = getCatsFromFile("data/cats.txt");
System.out.println("-----------Java File IO Example-----------");
for (Cat cat : myCats) {
// 这里调用了Cat的toString方法获取一个字符串直接打印
System.out.println(cat);
}
}
static List<Cat> getCatsFromFile(String fileName) {
/*
* 用Java自带的File IO 读写文件数据, 从cats.txt中读取数据:每一行对应一只cat的三个属性. 获取所有猫的对象,保存在列表中并返回
*/
// 通过fileName创建一个文件(句柄)
File file = new File(fileName);
List<Cat> cats = new ArrayList<>();
try { // Java的异常处理机制,try{}catch(Exception e){}
// 创建一个Scanner来逐行扫描文件
Scanner fileScanner = new Scanner(file);
// 如果扫描到文件有下一行
while (fileScanner.hasNext()) {
String line = fileScanner.nextLine(); // 读取一行
line = line.trim(); // 删除这一行首尾的空格
// 如果这一行(去除首位空格后)长度为0,或者以 # 开头,跳过
if (line.length() == 0 || line.startsWith("#")) {
continue;
}
// 通过','(或后面有空格) 分割字符串,得到一个字符串数组
String[] catProperties = line.split(",\\s*");
// 如果分割后,这一行不是包含三个属性(对应于name, breed, age),为不完整数据行,跳过
if (catProperties.length != 3) {
continue;
}
Cat cat = new Cat();
cat.name = catProperties[0].trim(); // 第0个属性为name,去除首尾空格
cat.breed = catProperties[1].trim(); // 第1个属性为breed,去除首尾空格
// 第三个属性去除首尾空格后,转换成整数,赋值给cat
cat.age = Integer.parseInt(catProperties[2].trim());
// 将cat添加进列表
cats.add(cat);
}
// 关闭scanner
fileScanner.close();
} catch (Exception e) {
e.printStackTrace();
}
// 返回列表
return cats;
}
}
此时,通过读取文件中的数据,创建cats就完成了。
接下来使用Java Stream来实现相同的功能,可以不看。
完整的Project下载,可在Eclipse直接导入
Extended:Java Stream的实现方式
JavaFileStreamIO.java
package javaio;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class JavaFileStreamIO {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Cat> cats = getCatsFromFileStream("data/cats.txt");
System.out.println("-----------Java File IO with Stream-------- ---");
for (Cat cat : cats) {
System.out.println(cat);
}
}
// Java Stream 编程实现
static List<Cat> getCatsFromFileStream(String fileName) {
List<Cat> cats = new ArrayList<>();
try {
cats = Files.lines(Path.of(fileName)) // 获取每一行组成的stream
.filter(line -> line.trim().length() > 0 // 筛选非空行
&& !line.startsWith("#") // 且不以#开头
&& line.split(",").length == 3) // 且合法(是3个属性)
.map(line -> { // 将一行转换成一个Cat object
String[] properties = line.split(",\\s*");
Cat cat = new Cat();
cat.name = properties[0];
cat.breed = properties[1];
cat.age = Integer.parseInt(properties[2].trim());
return cat;
}).collect(Collectors.toList()); // 收集每一行生成的Ojbect,输出一个List
} catch (Exception e) {
e.printStackTrace();
}
return cats;
}
}