IO流技术【Properties类介绍、文件切割与合并】
1、Properties类介绍
1.1、Properties的基本功能
Properties特点:
1、Hashtable的子类,map集合中的方法都可以用。
2、该集合没有泛型。键值都是字符串。
3、它是一个可以持久化
的属性集。键值可以存储到集合中,也可以存储到持久化的设备上。键值的来源也可以是持久化的设备。
4、有和流技术相结合的方法。load(InputStream) load(Reader) store(OutputStream,commonts); stroe(Writer,comments)
;
public static void methodDemo() {
// Properties的基本存和取。
// 1,创建一个Properties
Properties prop = new Properties();
prop.setProperty("zhangsan", "20");
prop.setProperty("lisi", "23");
prop.setProperty("wangwu", "21");
prop.list(System.out);// 用于调试。少用!
Set<String> set = prop.stringPropertyNames();
for (String name : set) {
String value = prop.getProperty(name);
System.out.println(name + "...." + value);
}
}
将配置文件中的数据存储到文件中
public static void methodDemo2() throws IOException {
Properties prop = new Properties();
prop.setProperty("zhangsan", "20");
prop.setProperty("lisi", "23");
prop.setProperty("wangwu", "21");
// 将集合中的数据持久化存储到设备上。
// 需要输出流对象。
FileOutputStream fos = new FileOutputStream("tempfile\\info.properties");
// 使用prop的store方法。
prop.store(fos, "my demo ,person info");
fos.close();
}
读取配置文件中的数据,同时更新数据并保存。
public static void methodDemo3() throws IOException {
File configFile = new File("tempfile\\info.properties");
// 读取流中的数据。
Properties prop = new Properties();
// 定义读取流和数据文件关联。
FileInputStream fis = new FileInputStream(configFile);
prop.load(fis);
prop.setProperty("zhangsan", "12");
// 要将改完的数据重新持久化。
FileOutputStream fos = new FileOutputStream(configFile);
prop.store(fos, "");
fos.close();
fis.close();
}
2、文件切割
2.1、文件切割思路
目前我们学习了IO
中的常用对象,接下来使用这些对象进行应用。
**需求:**文件切割,将一个比较大的文件切割成多个碎片文件。文件切割有2中方式。
第一种:指定具体切割成多少文件。
第二种:指定每个碎片的大小,直到把文件切割完成。
切割文件的应用场景:比如有些论坛指定上传的文件有大小限制。一个比较大的文件无法上传,这时就可以将其切割后上传,同时再别人下载后再将文件合并即可应用。
思路:
- 读取
源文件
,将源文件的数据分别复制到多个文件中。 - 切割方式有两种:按照碎片个数切,要么按照指定大小切。
- 一个输入流对应多个输出流。
- 每一个碎片都需要编号,顺序不要错。
- 将源文件以及切割的一些信息也保存起来随着碎片文件一起发送。
切割文件信息:
- 源文件的名称(文件类型)
- 切割的碎片的个数。
将这些信息单独封装到一个文件中。还要一个输出流完成此动作。
2.2、文件切割代码体现
public class SplitFileTest {
private static final int BUFFER_SIZE = 1048576;// 1024*1024
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws IOException {
File srcFile = new File("E:\\1.mp3");
File partsDir = new File("E:\\PartFiles");
splitFile(srcFile, partsDir);
}
/**
* 切割文件。
*/
public static void splitFile(File srcFile, File partsDir)
throws IOException {
// 健壮性的判断。
if (!(srcFile.exists() && srcFile.isFile())) {
throw new RuntimeException("源文件不是正确的文件或者不存在");
}
if (!partsDir.exists()) {
partsDir.mkdirs();
}
// 1,使用字节流读取流和源文件关联。
FileInputStream fis = new FileInputStream(srcFile);
// 2,明确目的。目的输出流有多个,只创建引用。
FileOutputStream fos = null;
// 3,定义缓冲区。1M.
byte[] buf = new byte[BUFFER_SIZE];// 1M
// 4,频繁读写操作。
int len = 0;
int count = 1;// 碎片文件的编号。
while ((len = fis.read(buf)) != -1) {
// 创建输出流对象。只要满足了缓冲区大小,碎片数据确定,直接往碎片文件中写数据 。
// 碎片文件存储到partsDir中,名称为编号+part扩展名。
fos = new FileOutputStream(new File(partsDir, (count++) + ".part"));
// 将缓冲区中的数据写入到碎片文件中。
fos.write(buf, 0, len);
// 直接关闭输出流。
fos.close();
}
/*
* 将源文件以及切割的一些信息也保存起来随着碎片文件一起发送。
*/
String filename = srcFile.getName();
int partCount = count;
// 创建一个输出流。
fos = new FileOutputStream(new File(partsDir, count + ".properties"));
fos.write(("filename=" + filename + LINE_SEPARATOR).getBytes());
fos.write(("partcount=" + Integer.toString(partCount)).getBytes());
fos.close();
fis.close();
}
}
2.3、读取配置文件信息
//Properties的基本功能public class ReaderPartConfigDemo {
public static void main(String[] args) throws IOException {
//解析partConfig文件中的信息。
File configFile = new File("E:\\PartFiles\\7.properties");
readPathConfig(configFile);
}
public static void readPathConfig(File configFile) throws IOException {
/*
* 配置文件规律,只要读取一行文本,按照 = 对文本进行切割即可。
*/
BufferedReader bufr = new BufferedReader(new FileReader(configFile));
String line = null;
while((line=bufr.readLine())!=null){
String[] arr = line.split("=");
System.out.println(arr[0]+":::::"+arr[1]);
}
bufr.close();
}Properties的基本功能
}
上述文件切割在保存和读取关于切割信息文件时,十分的繁琐。在Java中当需要保存和读取具备一定关联关系的数据时,可以使用Java提供前面提到的一个对象Properties
。
3、文件合并
3.1、记录行动次数
**需求:**定义一个功能,记录程序运行的次数。满足5次后,给出提示,试用次数已到,请注册!
思路:
1,需要计数器。
2,计数器的值生命周期要比应用程序的周期要长,需要对计数器的值进行持久化。count=1,里面存储的应该是键值方式,map集合,要和设备上的数据关联,需要IO技术。集合+IO = Properties。
public class Test {
public static void main(String[] args) throws IOException {
boolean b = checkCount();
if(b)
run();
}
public static boolean checkCount() throws IOException {
boolean isRun = true;
//1,将配置文件封装成File对象。因为要判断文件是否存在。
File configFile = new File("tempfile\\count.properties");
if(!configFile.exists()){//如果不存在,就创建。
configFile.createNewFile();
}
int count = 0;//记录住每次存储的次数。
Properties prop = new Properties();//用于存储配置文件中的数据。
//2,定义流对象。
FileInputStream fis = new FileInputStream(configFile);
//3,将流中的数据加载到集合中。
prop.load(fis);
//4,获取键对应的次数。
String value = prop.getProperty("count");
if(value!=null){
count = Integer.parseInt(value);
if(count>=5){
System.out.println("试用次数已到,请注册,给钱!");
isRun = false;
}
}
count++;//对取出的次数进行自增。
//将键count,和自增后值重新存储到集合中。
prop.setProperty("count", Integer.toString(count));
//将集合中的数据存储到配置文件中。
FileOutputStream fos = new FileOutputStream(configFile);
prop.store(fos, "");
fos.close();
fis.close();
return isRun;
}
public static void run(){
System.out.println("软件运行");
}
}
3.2、SequenceInputStream序列流
SequenceInputStream序列流:
**序列流特点:**流对象的有序的排列。
**序列流解决问题:**将多个输入流合并成一个输入流。将多个源合并成一个源。对于多个源的操作会变的简单。
**序列流功能:**特殊之处在构造函数上。一初始化就合并了多个流进来。
**使用场景:**对多个文件进行数据的合并。多个源对应一个目的。
public class SequenceInputStreamDemo {
public static void main(String[] args) throws IOException {
/*
* 演示序列流。SequenceInputStream。
*/
//如何获取一个Enumeration呢?Vector有,但是效率低,使用ArrayList。
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
//添加三个输入流对象,和指定的具体文件关联。
for(int x=1; x<=3; x++){
al.add(new FileInputStream("tempfile\\"+x+".txt"));
}
//怎么通过ArrayList获取枚举接口。可以使用Collections工具类中的方法。
Enumeration<FileInputStream> en = Collections.enumeration(al);
//创建序列流对象。需要传递Enumeration。
SequenceInputStream sis = new SequenceInputStream(en);
//创建目录。文件。
FileOutputStream fos = new FileOutputStream("tempfile\\4.txt");
//频繁的读写操作。
//1,创建缓冲区。
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
//关闭流
fos.close();
sis.close();
}
}
3.3、文件合并使用SequenceInputStream
对一个文件进行切割(一个源对应多个目的),切成碎片,在将碎片进行合并成原来的文件。
public class MergerFileTest3 {
public static void main(String[] args) throws IOException {
File partsDir = new File("E:\\PartFiles");
mergerFile(partsDir);
}
public static void mergerFile(File partsDir) throws IOException {
/*
* 合并问题如下:
* 1,如何明确碎片的个数,来确定循环的次数,以明确要有多少个输入流对象。
* 2,如何知道合并的文件的类型。
* 解决方案:应该先读取配置文件。
*/
//1,获取配置文件。
File configFile = getConfigFile(partsDir);
//2,获取配置文件信息容器。获取配置信息的属性集。
Properties prop = getProperties(configFile);
//3,将属性集对象传递合并方法中。
merge(partsDir,prop);
}
//根据配置文件获取配置信息属性集。
private static Properties getProperties(File configFile) throws IOException {
FileInputStream fis = null;
Properties prop = new Properties();
try{
//读取流和配置文件相关联。
fis = new FileInputStream(configFile);
//将流中的数据加载的集合中。
prop.load(fis);
}finally{
if(fis!=null){
try{
fis.close();
}catch(IOException e){
//写日志,记录异常信息。便于维护。
}
}
}
return prop;
}
//根据碎片目录获取配置文件对象。
private static File getConfigFile(File partsDir) {
if(!(partsDir.exists() &&partsDir.isDirectory())){
throw new RuntimeException(partsDir.toString()+",不是有效目录");
}
//1,判断碎片文件目录中是否存在properties文件。使用过滤器完成。
File[] files = partsDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith(".properties");
}
});
if(files.length!=1){
throw new RuntimeException("properties扩展名的文件不存在,或不唯一");
}
File configFile = files[0];
return configFile;
}
private static void merge(File partsDir,Properties prop) throws IOException {
//获取属性集中的信息。
String filename = prop.getProperty("filename");
int partCount = Integer.parseInt(prop.getProperty("partcount"));
//使用io包中的SequenceInputStream,对碎片文件进行合并,将多个读取流合并成一个读取流。
List<FileInputStream> list = new ArrayList<FileInputStream>();
for (int i = 1; i < partCount; i++) {
list.add(new FileInputStream(new File(partsDir, i + ".part")));
}
//怎么获取枚举对象呢?List自身是无法获取枚举Enumeration对象的,考虑到Collections中去找。
Enumeration<FileInputStream> en = Collections.enumeration(list);
//源。
SequenceInputStream sis = new SequenceInputStream(en);
//目的。
FileOutputStream fos = new FileOutputStream(new File(partsDir,filename));
//不断的读写。
byte[] buf = new byte[4096];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}