背景
关于 IO流只是在以前上课老师说要一个序列化的任务要完成,我才去学习了一下IO流去完成任务式的学了一下,之后任务完成也就没接着学习了,这导致后来一碰到IO相关的问题都两眼一抹黑,特别是在文件上传的后台处理的那部分,完全暴露了我对IO的操作的缺陷。于是前两天我重新复习了一遍java中的IO操作。
本次复习的地址为 How2J
文件
文件的操作在java中有以下常用操作:
file.getParentFile(); //获取父文件对象
file.exsits(), //文件存在的boolean值 ,存在为true ,反之为false。
file.mkdir();//创建文件夹,若上级路径不存在,创建就无效
file.mkdirs();//创建文件夹,若上级路径不存在,则会一并创建
file.createNewFile(); //创建文件
值得一提的是,若是在创建文件时,file的上一级目录不存在,则会抛出异常,所以在创建文件前我都会以下操作:
File parent = file.getParentFile();
if(parent != null && !parent.exists() ){
parent.mkidrs();
file.createNewFile();
}
保证不会抛出异常。
file.getAbsolutePath();//获取文件的绝对路径
file.listFile();// 以 **文件数组** 的形式返回本对象(文件夹)下的所有文件的对象
file.length(); //文件的大小 字节为单位 返回类型int
file.isFile(); //是文件 文件返回true 文件夹返回false
file.isDirectory(); //是文件夹的boolean 文件夹返回true 文件返回false
file.lastModified(); //文件最后修改时间 返回类型为long。
流的基础
1 何为流:
Java中的流是个抽象的概念,当程序需要从某个数据源读入数据的时候,就会开启一个数据流,数据源可以是文件、内存或网络等等。相反地,需要写出数据到某个数据源目的地的时候,也会开启一个数据流,这个数据源目的地也可以是文件、内存或网络等等(摘自百度)
2:
流的初步分类:字节流(byte流),字符流(char流)。
流向来分类的话为:输入流 和 输出流。这里的 输入 输出 的对象为 电脑的程序,故 输入流 是 从 其余地方(如磁盘) 将数据 输入到程序 ,输出流 是将程序的 数据 输出 到 其余地方(如磁盘)。
2:字节流(byte流)的初步使用
可以写以下的几道小小的demo熟练。
1:拆分文件(如将一个 图片文件 按字节数拆分为若干个子文件)。
2:合并文件, 将上面拆分的文件合并成 一个图片文件,再点击查看,观察图片文件是否被破坏。
3:将 一个 文件 copy到 另外一个文件夹(此文件夹事先不存在)中。
4:将 一个文件夹 下的所有文件 复制 到 另外一个文件夹下。
下面给出demo4的代码:
// path 为 复制的文件 存储的路径 ,file为 要复制的文件夹 的文件对象
public static void coptDerictory(String path,File file){
String name = "copy-"+file.getName();
//若 file 为文件夹
if(file.isDirectory()){
//获取file 下的所有 文件对象
File[] files = file.listFiles();
//复制的文件夹的路径
path += File.separator+name;
//创建复制的文件夹
File parentFile = new File(path);
parentFile.mkdir();
//递归创建文件夹下的文件
for ( File file1 : files){
coptDerictory(path,file1);
}
}else{
//若 file 为文件
File file1 = new File(path+File.separator+name);
try {
//创建文件
file1.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try(
//输入输出流
//此处定义的流 都是final 且在执行完try catch 后 自动关闭
FileInputStream fileInputStream = new FileInputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream(file1)
)
{
byte[] bytes = new byte[1024 * 5];
int len = 0;
//输出
while ( (len = fileInputStream.read(bytes)) != -1 ){
fileOutputStream.write(bytes,0,len);
}
//强制刷出
fileOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
有几个要注意的点:
1:File.separator 为 不定的 文件的分割符 , 在windows 下 为‘\’,在linux下为‘/’,故在以后写分割符时,尽量使用File.separator。
2:在使用fileOutputStream.write();方法时,一开始 我使用的是:fileOutputStream.write(byte[]);但是这样会导致可能源文件已经结束了,但是复制出来的文件中在本应该结束的地方多余出来一些文本 ,这是因为此方法会将byte数组所有的都写入磁盘,但是,上一次的读入可能仅读入x长度的byte,x之后的byte为上上次读取的废弃byte,此时再全部写入磁盘就会导致多写数据到新文件(复制出来的文件),解决的方法是使用fileOutputStream.write(byte[],int begin,int end);如此一来,解决问题。
3:在try 后方的括号中 定义的 流 在 try catch 块完毕后会自动关闭。
3字符流初始用
字符流有以下(未包含全部,本次复习主要包含以下):
FileReader
FileWriter
BufferedReader
PrintWriter
ObjectInputStream
ObjectOutputStream
DataInputStream
DataOutputStream
下方的输入流(BufferedReader,ObjectInputStream,DataInputStream)的构造函数 均需以流作为 入参,如:
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
基本操作可以去 How2J 去稍作学习。
以下几个小demo可以帮你迅速掌握 字符流:
1:使用字符流将文件输出到控制台。
2:写入一些字符到文件中。
3:将文件加密(加密规则可以 如:* 数字:
* 如果不是9的数字,在原来的基础上加1,比如5变成6, 3变成4
* 如果是9的数字,变成0
* 字母字符:
* 如果是非z字符,向右移动一个,比如d变成e, G变成H
* 如果是z,z->a, Z-A。
* 字符需要保留大小写
* 非字母字符
* 比如’,&^ 保留不变,中文也保留不变)。
4:使用BufferedReader读取有规律的数据进行操作(如,将一本小说按章节拆分)。
5:ObjectOutputStream 持久化 对象。
6:PrintWriter 向文件中写入字符串。
7:DataOutputStream 持久化数据,DataInputStream读取。
8:查找指定文件夹下 内容带关键字的文件。
下面为demo8的代码:
/**
* 在 file 文件夹下找到 。java后缀的文件
* @param file
* @return
*/
public static List<File> findJavaFile(File file){
List<File> fileList = new ArrayList<>();
//System.out.println("file.getName() = " + file.getName());
if(file.isDirectory()){
File[] files = file.listFiles();
for(File file1 : files){
if(file1.isDirectory()){
List<File> temp = findJavaFile(file1);
if(temp != null){
fileList.addAll(temp);
}
}else {
if(file1.getName().indexOf(".java") > 0){
fileList.add(file1);
}
}
}
}
return fileList;
}
/**
* 从fileList中找到文件内容带关键字的文件
* @param fileList
* @param word
* @return
*/
public static List<File> constanitWord(List<File> fileList,String word){
List<File> files = new ArrayList<>();
for (File f : fileList) {
try(FileReader fileReader = new FileReader(f);
BufferedReader bufferedReader = new BufferedReader(fileReader)
) {
String line = null;
while( (line = bufferedReader.readLine()) != null ){
if(line.indexOf(word) > 0){
files.add(f);
break;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return files;
}
注意:
1:ObjectInputStream 和 DataInputStream 两个在读入时 是没有 结束 标志的,解决方法有两种:
若文件中有若干个Object对象,你用ObjectInputStream中的readObject()去读,何时判读到结尾?
方法之一:(常用的方法)将若干个对象(数量不定)都装入一个容器中(如:ArrayList之类),
然后将容器这一个对象写入就行了。读取时,只要读取一个对象(即容器对象)就行了。
方法之二:(若不想用容器),则由于数量不定,正是用EOFException来判断结束。
值得一提的是 EOFException 是 IOException,故catch时应放在IOException前catch。
2:要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。
本次的复习以how2J上的教程来的,故本片博客的demo部分和上面教程的练习一样,侵删。