IO技术学习笔记
IO技术核心:
三口:Closeable,Flushable,Serializable
五类:File,InputStream,OutputStream,Reader,Writer
ApI阅读方法:
1.看继承体系,简短说明。
2.看构造器,若无构造器,则为工具类或具有静态方法。
3.看方法,重点关注方法的名字与形参,及返回形式,是否为静态方法。
流的概念:
按流的方向分类:
1. 输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)。
2. 输出流:数据流向是程序到目的地(以OutPutStream、Writer结尾的流)。
程序相当于是一个中介(中心),以程序为基础判断是输入流(读)还是输出流(写)。
按处理的数据单元分类:
1. 字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、FileOutputStream。
2. 字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter。
字符流的底层还是字节流,因为计算机只认识二进制数据。字符集是二者转化的“字典”。
按处理对象不同分类:
1. 节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等,名字中带着File/Byte。
2. 处理流:(基于节点流)不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。
节点流处于IO操作的第一线,所有操作必须通过它们进行;处理流可以对节点流进行包装,提高性能或提高程序的灵活性。
File类:
Java无法直接操纵文件,因此file类是抽象的,表示java与文件之间的联系,实际上文件可能不存在。
File.separator:分隔符
路径的3种书写方式:1."C:\\Users\\Dexin\\Desktop\\java练习\\javaexercise\\chapter10\\1.png"(必须是双杠)
2."C:"+File.separator+"Users"+File.separator+"Dexin"+File.separator+"Desktop"+File.separator+"java练
习"+File.separator+"javaexercise"+File.separator+"chapter10"+File.separator+"1.png"
3."C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/1.png"(推荐)
利用File类的多样构造方法来构造对象:
1.File(path)
例:String path = "C:\\Users\\Dexin\\Desktop\\java练习\\javaexercise\\chapter10\\1.png";
File src1 =new File(path);
2.File(String parent, String child)
例:File src2 = new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10","1.png");
File src3 = new File("C:/Users/Dexin","Desktop/java练习/javaexercise/chapter10/1.png");
3.File(File parent, String child)
例:File src4 = new File(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10"),"1.png");
带盘符的成为绝对路径,不带的成为相对路径。
可用getAbsolutePath()方法获得绝对路径。
File类相关方法:
1.getName():获取文件名称
getPath():获取文件路径,给的是什么路径就返回什么路径
getAbsolutePath():获取文件绝对路径
getParent():获取父路径,将给的路径去除文件名
2.exists()文件是否存在
isFile()判断是否是文件
isDirectory()判断是否是文件夹
3.length()获取文件长度,若文件不存在或不是文件,返回0(我测试的时候文件夹返回4096)
4.createNewFile()根据File类对象创建实际文件(不存在才能创建)
delete()删除实际文件(存在实际文件才能删除)
几个重要的小练习:
1.用递归的方法分级列出文件夹下的文件(常用类中已做)
2.用递归的方法计算文件夹的大小。重点在于如下方法:
3.用自定义对象的方式计算文件夹的大小。在创建该类的对象时,已经创建了指定path的File类对象,并自动执行了count方法。只要用get方法获取length即可。(count方法与上面相同,略写)
4.用自定义对象的方式计数指定文件夹中的文件与文件夹个数。
在3的基础之上,定义filesize与dirsize,并分别写getters。修改count方法,若为文件,filesize++,文件夹则dirsize++。最后在main函数中打印getFilesize与getDirsize即可。
字符集:
相当于字节与字符转换的字典。
常用的字符集:1.GBK 默认字符集,中文2个字节,英文1个
2.UTF-16LE 定长,中英文均2个字节
3.UTF-8 变长,中文3个字节,英文1一个
编码格式:byte[ ] 字节对象名称 = 指定字符串名称.getBytes("字符集名称");
可用length()方法查看字节对象的长度
解码格式:String 字符串名称=new String(字节对象名称,起始位,长度,"字符集名称");
乱码可能的原因:1.字节数不够
2.字符集不统一
InputStream:字节输入流的父类,常用方法read()(读取字节),close()(关闭流,释放相关系统资源)
OutputStream:字节输出流的父类,有Flushable接口(刷新),常用方法write(int),flush(),close()
Reader:字符输入流的父类,有Readable接口,常用方法read(),close()
Writer:字符输出流的父类,有Flushable接口(刷新),Appendble接口(从尾部追加),常用方法write(String),flush(),close()
IO程序经典步骤:1.确定数据源 2.确定流 3.读/写操作 4.释放系统资源
使用FileInputStream读取字节:
1.int read():逐个字节读取,返回的就是字节代码,(char)强制转型即可查看。
File src = new File("d:/a.text");//确定数据源头 InputStream is =null; try { is = new FileInputStream(src); int temp; while((temp=is.read())!=-1) { System.out.println((char)temp); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { if(null!=is) { is.close(); } } catch (IOException e) { e.printStackTrace(); } }
2.int read(byte [ ] 数组名称):将字节按指定长度缓存在数组中,返回的是实际读取的字节数(若读完则返回-1),通过解码才能查看。
操作部分步骤总结:建立缓存数组byte[ ],并指定规格(一般为1024的倍数)。定义实际读入长度(用来判断是否读完)。写一个while循环,用read()方法将输入流对象中的内容读到缓存数组中。若读完(length为-1),则我们得到一组字节数据。接着用String方法进行解码,将得到的字符数据存入字符串中。然后可以打印这个字符串。
1 File src =new File("d:/a.text"); 2 InputStream is = null; 3 try { 4 is = new FileInputStream(src); 5 byte [] flush = new byte[1024];//一次性缓存1024个字节在flush中 6 int length; 7 while((length=is.read(flush))!=-1) { 8 String str = new String(flush,0,length);//解码 9 System.out.println(str); 10 } 11 } 12 catch (FileNotFoundException e) { 13 e.printStackTrace(); 14 } 15 catch (IOException e) { 16 e.printStackTrace(); 17 }finally { 18 if(null!=is) { 19 try { 20 is.close(); 21 } catch (IOException e) { 22 e.printStackTrace(); 23 } 24 } 25 }
注意:用read()方法读到程序里的是字节
使用FileOutputStream写字节:
一开始指定的数据源可以不存在,创建输出流对象时可指定写的方法(true附加,false覆盖)。在写操作之后要用flush()方法刷新一下。
操作部分步骤总结:指定一个字符串作为写的内容。将其用getBytes()方法编码,将编码后的字节并存放在一个byte[ ]数组中。用write()方法将字节数组写到输出流对象中,最后flush()刷新一些即可。
1 File src = new File("d:/b.text");//可以不存在 2 OutputStream os = null; 3 try { 4 os = new FileOutputStream(src,true);//true表示追加写,false表示覆盖写 5 String msg = "I love you"; //指定写入的语句 6 byte[]data=msg.getBytes();//编码 7 os.write(data,0,data.length);//将编码完的字节数组写到os流对象中 8 os.flush();//刷新,这是个好习惯 9 } 10 catch (FileNotFoundException e) { 11 e.printStackTrace(); 12 } 13 catch (IOException e) { 14 e.printStackTrace(); 15 }finally { 16 if(null!=os) { 17 try { 18 os.close(); 19 } catch (IOException e) { 20 e.printStackTrace(); 21 } 22 } 23 }
注意:用write()方法写的内容也必须是字节。
FileInputStream,FileInputStream综合运用 拷贝文件:
即以程序为中介,先读入,后写出,完成拷贝。
注意最后释放系统资源时,先打开的流对象后关闭。
1 public static void copy(String srcpath,String destpath) { 2 File src = new File(srcpath); 3 File dest = new File(destpath); 4 InputStream is =null; 5 OutputStream os = null; 6 try { 7 is = new FileInputStream(src); 8 os = new FileOutputStream(dest); 9 byte[]flush=new byte[1024]; 10 int length=-1; 11 while((length=is.read(flush))!=-1) {//读 12 os.write(flush,0,length);//写 13 } 14 os.flush();//刷新 15 } catch (FileNotFoundException e) { 16 e.printStackTrace(); 17 } 18 catch (IOException e) { 19 e.printStackTrace(); 20 }finally { 21 if(null!=os) { 22 try { 23 os.close(); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 } 27 } 28 if(null!=is) { 29 try { 30 is.close(); //先打开的后关闭 31 } catch (IOException e) { 32 e.printStackTrace(); 33 } 34 } 35 36 } 37 38 }
使用FileReader读取字符:
创建缓存字符数组char[ ],并指定规格。定义实际读入长度(用来判断是否读完)。写一个while循环,用read()方法将输入流对象中的内容读到缓存字符数组中。若读完(length为-1),则我们得到一组字符数据,可以直接打印这个数组。这个过程中没有字节的参与,均为字符。
1 File src = new File("d:/a.text"); 2 Reader rd =null; 3 try { 4 rd = new FileReader(src); 5 char[]flush = new char[1024]; 6 int length; 7 while((length=rd.read(flush))!=-1) { 8 System.out.println(flush); 9 } 10 } 11 catch (FileNotFoundException e) { 12 e.printStackTrace(); 13 } 14 catch (IOException e) { 15 e.printStackTrace(); 16 }finally { 17 if(null!=rd){ 18 try { 19 rd.close(); 20 } catch (IOException e) { 21 e.printStackTrace(); 22 } 23 } 24 }
使用FileWriter写字符:
方法1:指定一个字符串。用toCharArray()方法将字符串存在char[ ]数组中。用write方法将数组中的内容写入流对象中。
方法2:指定一个字符串。直接用write()方法将字符串写入流对象中。
方法3:直接用append()方法将字符串写入流对象中。
这个过程中没有字节的参与,均为字符。
1 File dest = new File("d:/a.text"); 2 Writer wt =null; 3 try { 4 wt = new FileWriter(dest); 5 //写法1 6 //String str = "努力学习"; 7 //char[] data = str.toCharArray(); 8 //wt.write(data,0,data.length); 9 //写法2 10 //String str = "更加努力学习"; 11 //wt.write(str); 12 //写法3 13 wt.append("每天").append("都要").append("努力学习"); 14 wt.flush(); 15 } catch (IOException e) { 16 e.printStackTrace(); 17 }finally { 18 if(null!=wt) { 19 try { 20 wt.close(); 21 } catch (IOException e) { 22 e.printStackTrace(); 23 } 24 } 25 }
使用ByteArrayInputStream读取字节数组:
与FileInputStream的不同之处在于,它的数据源是一个字节数组(指定字符串后,用getBytes()方法获得)。Byte类的流都不需要释放资源。
1 byte[] src = "I love you so much".getBytes();//数据源是一个字节数组 2 InputStream is = new ByteArrayInputStream(src); 3 byte[]flush = new byte[20]; 4 int length; 5 try { 6 while((length=is.read(flush))!=-1) { 7 String str = new String(flush,0,length); 8 System.out.println(str); 9 } 10 } catch (IOException e) { 11 e.printStackTrace(); 12 } 13 }
使用ByteArrayOutputStream向内存写一段字节:
不需要指定目的地,创建一个字节数组置为null即可。指定一段字符串后,用getBytes()方法编码放入字节数组中,再写入流中。
最后我们需要通过toByteArray()方法将流中的字节放入目的地数组中,才可以打印查看。因为这是ByteArrayOutputStream新增的方法,因此一开始创建流对象时不可以使用多态。
1 byte [] dest = null;//不指定目的地 2 try { 3 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 4 String str = "I love you"; 5 byte[] data=str.getBytes();//编码 6 baos.write(data,0,data.length);//将编码完的字节数组写入流中 7 baos.flush(); 8 dest=baos.toByteArray(); 9 System.out.println(dest.length); 10 System.out.println(new String(dest,0,dest.length)); 11 } catch (IOException e) { 12 e.printStackTrace(); 13 }
释放资源程序的优化:
1.普通封装为:public static void close(Inputstream is,OutputStream os)
作用是释放了关于这两个流的资源。
2.使用Closeable接口关闭多个流的封装
1 public static void close(Closeable...ios) { 2 for(Closeable io:ios) { 3 try { 4 if(null!=io) { 5 io.close(); 6 } 7 } 8 catch (IOException e) { 9 e.printStackTrace(); 10 } 11 } 12 }
3.try with resource
只须去掉finally所有内容,在try后添加(),括号中写需要释放资源的流即可,多个流用分号隔开,如:try (is;os)
装饰器结构:
1.抽象组件:待装饰对象的类的接口。定义一个接口(饮料),其中定义一些方法(价格,信息)。
2.具体组件:待装饰对象的类(咖啡),是1的实现类。可以在其中定义私有信息(咖啡名称),并对1中所有方法提供实现(价格10元,信息输出咖啡名称)。
3.抽象装饰类:装饰对象的类的抽象类,也是1的实现类。相当于给了一个装饰方法的模板。先定义一个1的对象(饮料对象),并且传入构造方法,然后实现1中的方法,返回的是这里定义的对象调用的方法(this.饮料对象.价格/信息)。
4.具体装饰类:具体装饰物(牛奶,糖果),继承3。构造方法中传入3中定义的对象(饮料对象)。实现方法中返回3中调用方法并可作一定的操作(super.价格*4/信息“加入了牛奶”)
main函数中,创建一个具体组件的对象。加装饰时,先创建具体装饰类的对象,并传入具体组件对象的名称,就可以打印具体的方法了。
1 public class DecorateDrink { 2 public static void main(String[] args) { 3 drink cf = new coffee(); 4 //加牛奶 5 drink m = new milk(cf); 6 System.out.println(m.info()); 7 System.out.println(m.cost()); 8 //加糖 9 drink s = new sugar(cf); 10 System.out.println(s.info()); 11 System.out.println(s.cost()); 12 //混合 13 drink ms = new milk(cf);//加完牛奶 14 ms=new sugar(m); //再加糖,成为ms 15 System.out.println(ms.info()); 16 System.out.println(ms.cost()); 17 } 18 } 19 //抽象组件 20 interface drink{ 21 double cost(); 22 String info(); 23 } 24 //具体组件 25 class coffee implements drink{ 26 private String name = "原味咖啡"; 27 public double cost() { 28 return 10; 29 } 30 public String info() { 31 return name; 32 } 33 } 34 //抽象装饰类 35 abstract class decorate implements drink{ 36 private drink dk; 37 public decorate(drink dk) { 38 super(); 39 this.dk = dk; 40 } 41 public double cost() { 42 return (this.dk.cost()); 43 } 44 public String info() { 45 return (this.dk.info()); 46 } 47 } 48 //具体装饰类1 49 class milk extends decorate{ 50 public milk(drink dk) { 51 super(dk); 52 } 53 public double cost() { 54 return (super.cost()*4); 55 } 56 public String info() { 57 return (super.info()+"加入了牛奶"); 58 } 59 60 } 61 //具体装饰类2 62 class sugar extends decorate{ 63 public sugar(drink dk) { 64 super(dk); 65 } 66 public double cost() { 67 return (super.cost()+5); 68 } 69 public String info() { 70 return (super.info()+"加入了糖"); 71 } 72 73 }
BufferedInputStream&BufferedOutputStream:
作用是对字节流进行缓冲,提高效率。默认缓冲区为8k,可以自己设定。
简便使用格式为:InputStream 流名称 = new BufferedInputStream(new FileInputStream(文件对象名称));
OutputStream 流名称 = new BufferedOutputStream(new FileOutputStream(文件对象名称));
如果要释放流资源,应先释放里面的,再释放外面的,处理流也可以不释放。
BufferedReader&BufferedWriter:
BufferedReader新方法:readLine()读一行字符,可以代替缓冲区
BufferedWriter新方法:newLine()可以代替换行。
由于有新方法,因此不能用多态。
注意使用try with resource来释放流资源。
以下是一个应用了BufferedReader与BufferedWriter的copy程序:
1 File src = new File("d:/a.text"); 2 File dest = new File("d:/copy of a.text"); 3 try( BufferedReader bfr = new BufferedReader(new FileReader(src)); 4 BufferedWriter bfw = new BufferedWriter(new FileWriter(dest));) 5 { 6 String line = null; 7 while((line = bfr.readLine())!=null) { 8 bfw.write(line); 9 bfw.newLine(); 10 } 11 bfw.flush(); 12 } 13 catch (FileNotFoundException e) { 14 e.printStackTrace(); 15 } 16 catch (IOException e) { 17 e.printStackTrace(); 18 }
InputStreamReader&OutputStreamWriter:
实现了将字节流转化成字符流。
System.in是字节流对象,代表键盘的输入,如果我们想按行接收用户的输入时,就必须用到缓冲字符流BufferedReader特有的方法readLine(),但是经过观察会发现在创建BufferedReader的构造方法的参数必须是一个Reader对象,这时候我们的转换流InputStreamReader就派上用场了。
而System.out也是字节流对象,代表输出到显示器,按行读取用户的输入后,并且要将读取的一行字符串直接显示到控制台,就需要用到字符流的write(String str)方法,所以我们要使用OutputStreamWriter将字节流转化为字符流。
1 try (BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in)); 2 BufferedWriter bfw = new BufferedWriter(new OutputStreamWriter(System.out));){ 3 String msg = ""; 4 while(!msg.equals("exit")) { 5 msg=bfr.readLine(); 6 bfw.write(msg); 7 bfw.newLine(); 8 bfw.flush(); 9 } 10 } 11 catch (IOException e) { 12 e.printStackTrace(); 13 }
1 try (BufferedReader bfr = new BufferedReader 2 (new InputStreamReader 3 (new URL("http://www.baidu.com").openStream(),"UTF-8")); 4 BufferedWriter bfw = new BufferedWriter 5 (new OutputStreamWriter 6 (new FileOutputStream("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/baidu.html"),"UTF-8"));){ 7 String msg = ""; 8 while((msg=bfr.readLine())!=null) { 9 bfw.write(msg); 10 bfw.newLine(); 11 bfw.flush(); 12 } 13 } catch (UnsupportedEncodingException e) { 14 e.printStackTrace(); 15 } catch (MalformedURLException e) { 16 e.printStackTrace(); 17 } catch (IOException e) { 18 e.printStackTrace(); 19 }
DataInputStream&DataOutputStream:
数据流将“基本数据类型与字符串类型”作为数据源,从而允许程序以与机器无关的方式从底层输入输出流中操作Java基本数据类型与字符串类型。
DataInputStream:从输入流中读取基本数据类型的数据
DataOutputStream:将基本数据类型的数据写入输出流
心得:任何类型的数据都可以通过toByteArray()方法转化为字节数组。
1 ByteArrayOutputStream baos= new ByteArrayOutputStream(); 2 DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos)); 3 dos.writeUTF("努力学习"); 4 dos.writeInt(18); 5 dos.writeBoolean(false); 6 dos.writeChar('a'); 7 dos.flush(); 8 byte[] datas = baos.toByteArray();//转化为字节数组 9 System.out.println(datas.length); 10 11 DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));//读出基本类型 12 String msg = dis.readUTF(); 13 int age = dis.readInt(); 14 Boolean flag = dis.readBoolean(); 15 char ch = dis.readChar();
此处忽略了异常的处理,可以直接throw。
ObjectInputStream&ObjectOutputStream:
ObjectOutputStream:把Java对象转换为字节序列的过程称为对象的序列化。
ObjectInputStream:把字节序列恢复为Java对象的过程称为对象的反序列化。
与数据流用法类似(因为所有数据类型都是对象),需要先写出(序列化),再读入(反序列化)
不仅可以将八大基本数据类型的数据转化成字节序列后再读,也可以操作各种对象。
特别注意:
只有实现了Serializable接口的对象才能被序列化,java自带的类的对象一般都有。但是,在操作自定义类的对象时(javabean),一定要手动实现java.io.Serializable接口。若某些属性不想被序列化,可以加上transient关键字。
最后打印的时候,要先判断读入(反序列化)后的对象是不是指定类型的对象,若是,强制转型后打印。
1 public class ObjectStream { 2 3 public static void main(String[] args) throws ClassNotFoundException, IOException { 4 ByteArrayOutputStream baos= new ByteArrayOutputStream(); 5 ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos)); 6 oos.writeUTF("努力学习"); 7 oos.writeInt(18); 8 oos.writeBoolean(false); 9 oos.writeChar('a'); 10 oos.writeObject("天天向上"); 11 oos.writeObject(new Date()); 12 employee emp = new employee("小明",30000); 13 oos.writeObject(emp); 14 oos.flush(); 15 byte[] datas = baos.toByteArray();//转化为字节数组 16 System.out.println(datas.length); 17 18 ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas))); 19 String msg = ois.readUTF(); 20 int age = ois.readInt(); 21 Boolean flag = ois.readBoolean(); 22 char ch = ois.readChar(); 23 Object study = ois.readObject(); 24 Object date = ois.readObject(); 25 Object empp = ois.readObject(); 26 27 28 if(flag instanceof Boolean) { 29 Boolean booleanObj =(Boolean)flag; 30 System.out.println(booleanObj); 31 } 32 if(study instanceof String) { 33 String strObj =(String)study; 34 System.out.println(strObj); 35 } 36 if(date instanceof Date) { 37 Date dateObj =(Date)date; 38 System.out.println(dateObj); 39 } 40 if(empp instanceof employee) { 41 employee strObj =(employee)empp; 42 System.out.println(strObj); 43 } 44 } 45 46 } 47 48 class employee implements java.io.Serializable{//一定要实现序列化接口 49 private String name; 50 private double salary; 51 public employee(String name, double salary) { 52 super(); 53 this.name = name; 54 this.salary = salary; 55 } 56 public employee() { 57 58 } 59 public String getName() { 60 return name; 61 } 62 public void setName(String name) { 63 this.name = name; 64 } 65 public double getSalary() { 66 return salary; 67 } 68 public void setSalary(double salary) { 69 this.salary = salary; 70 } 71 72 }
PrintStream:
打印流,方便打印。可通过setOut()方法来定向打印位置。
1 PrintStream ps =System.out; 2 ps.println("努力学习java");//写到控制台 3 ps = new PrintStream(new BufferedOutputStream(new FileOutputStream("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/print.txt")),true);//true即为自动刷新 4 ps.println("坚持学习java");//写入指定文件 5 //重定向输入端到指定文件 6 System.setOut(ps); 7 System.out.println("已重定向至指定文件"); 8 //重定向回控制台 9 System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)),true)); 10 System.out.println("已重定向回控制台");
RandomAccessFile:
随机访问流。创建RandomAccessFile流对象后,可用seek()方法来设置从第几个元素开始访问。
综合应用——分割文件:
SequenceInputStream可将多个输入流合并到一个容器中。
1 import java.io.File; 2 import java.io.FileInputStream; 3 import java.io.FileOutputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.OutputStream; 7 import java.io.RandomAccessFile; 8 import java.io.SequenceInputStream; 9 import java.util.ArrayList; 10 import java.util.List; 11 import java.util.Vector; 12 import java.io.BufferedInputStream; 13 import java.io.BufferedOutputStream; 14 15 public class SplitFile { 16 private File src; //源头文件 17 private String destDir; //目的地(文件夹) 18 private List <String> destPaths;//所有分割后的文件存储路径 19 private int blockSize; //每块大小 20 private int size;//块数 21 public SplitFile(String srcpath,String destDir, int blockSize) { 22 super(); 23 this.src = new File(srcpath);//源文件地址 24 this.destDir = destDir;//目的地(文件夹) 25 this.blockSize = blockSize;//每块大小 26 this.destPaths=new ArrayList<String>();//所有分割后的文件存储路径 27 init(); 28 } 29 //初始化:根据文件总长度与每块长度计算总块数,为分割后的文件命名 30 private void init() { 31 long length = this.src.length(); //文件总长度 32 this.size = (int)Math.ceil(length*1.0/blockSize); //定义块数 33 for(int i=0;i<size;i++) { 34 this.destPaths.add(this.destDir+ "/"+ i + "-"+ this.src.getName()); 35 } 36 } 37 //分割方法:分情况处理每块的实际长度,再具体分割 38 public void split() throws IOException { 39 long length = src.length(); //文件总长度 40 int beginPos = 0; 41 int actualSize = (int)(blockSize>length?length:blockSize); //如果不足一块,则只取一次,实际长度即为总长度,否则按块取 42 for(int i=0;i<size;i++) { 43 beginPos=i*blockSize; 44 if(i==size-1) {//最后一块 45 actualSize=(int)length; 46 }else { 47 actualSize=blockSize; 48 length-=blockSize; 49 } 50 splitDetail(i,beginPos,actualSize); 51 } 52 } 53 //具体分割方法:从源文件中向目标地址分块写内容 54 private void splitDetail(int i,int beginPos,int actualSize) throws IOException { 55 RandomAccessFile raf = new RandomAccessFile(this.src,"r"); 56 RandomAccessFile raf2 = new RandomAccessFile(this.destPaths.get(i),"rw"); 57 raf.seek(beginPos); 58 byte[] flush = new byte[1024]; 59 int length=-1; 60 while((length=raf.read(flush))!=-1) { 61 if(actualSize>length) { 62 raf2.write(flush,0,length); 63 actualSize-=length; 64 }else { 65 raf2.write(flush,0,actualSize); 66 break; 67 } 68 69 }raf2.close(); 70 raf.close(); 71 } 72 //合并 73 public void merge(String destPath) throws IOException{ 74 //输出流 75 OutputStream os = new BufferedOutputStream(new FileOutputStream(destPath,true)); 76 Vector<InputStream>vi=new Vector<InputStream>(); 77 SequenceInputStream sis = null; 78 //输入流 79 for(int i=0;i<destPaths.size();i++) { 80 vi.add(new BufferedInputStream(new FileInputStream(destPaths.get(i)))); 81 } 82 sis=new SequenceInputStream(vi.elements()); 83 byte [] flush = new byte[1024];//一次性缓存1024个字节在flush中 84 int length=-1; 85 while((length=sis.read(flush))!=-1) { 86 os.write(flush,0,length); 87 } 88 os.flush(); 89 sis.close(); 90 os.close(); 91 } 92 public static void main(String[] args) throws IOException { 93 SplitFile sf = new SplitFile("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/TestCopy.java","C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10",1024); 94 sf.split(); 95 sf.merge("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/Fdx.java"); 96 } 97 }
CommosIO:
资源下载:http://commons.apache.org/proper/commons-io/download_io.cgi
下载Binaries中的commons-io-2.6-bin.zip。解压后,将commons-io-2.6.jar与commons-io-2.6-sources.jar文件拷贝到程序所在工程的一个新建文件夹下。将commons-io-2.6.jar添加至路径后,即可使用。若想查看源码,写出想查看的方法名,ctrl+单击方法名,将commons-io-2.6-sources.jar加入即可。
1.查看文件/文件夹的大小:FileUtils.sizeOf()方法
1 long length = FileUtils.sizeOf(new File("d:/a.text")); 2 System.out.println(length); 3 length = FileUtils.sizeOf(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10")); 4 System.out.println(length);
2.获取列表文件:listFiles()方法
1 Collection <File> files= FileUtils.listFiles(new File("D:/PCGame"), 2 EmptyFileFilter.NOT_EMPTY,null);//只读一级文件 3 for(File file:files) { 4 System.out.println(file.getAbsolutePath()); 5 } 6 System.out.println("------------------------------------------"); 7 files= FileUtils.listFiles(new File("D:/PCGame"), 8 EmptyFileFilter.NOT_EMPTY,DirectoryFileFilter.INSTANCE);//深入阅读子孙级文件 9 for(File file:files) { 10 System.out.println(file.getAbsolutePath()); 11 } 12 System.out.println("------------------------------------------"); 13 files= FileUtils.listFiles(new File("D:/PCGame"), 14 new SuffixFileFilter("txt"),DirectoryFileFilter.INSTANCE);//过滤出指定类型的文件 15 for(File file:files) { 16 System.out.println(file.getAbsolutePath()); 17 }
3.读取文件内容:readFileToString(),readFileToByteArray(),readLines(),lineIterator()方法
1 //读取文件 2 String msg = FileUtils.readFileToString(new File("C:\\Users\\Dexin\\Desktop\\java练习\\javaexercise\\chapter10\\print.txt"),"GBK"); 3 System.out.println(msg); 4 //存于byte数组后再读取 5 byte[]datas = FileUtils.readFileToByteArray(new File("C:\\Users\\Dexin\\Desktop\\java练习\\javaexercise\\chapter10\\print.txt")); 6 System.out.println(datas.length); 7 //逐行读取 8 List<String>msgs= FileUtils.readLines(new File("C:\\Users\\Dexin\\Desktop\\java练习\\javaexercise\\chapter10\\print.txt"),"GBK"); 9 for(String str:msgs) { 10 System.out.println(str); 11 } 12 //用迭代器逐行读取 13 LineIterator it = FileUtils.lineIterator(new File("C:\\Users\\Dexin\\Desktop\\java练习\\javaexercise\\chapter10\\print.txt"),"GBK"); 14 while(it.hasNext()) { 15 System.out.println(it.nextLine()); 16 }
4.写一些内容至指定文件:write(),writeStringToFile(),writeByteArrayToFile(),writeLines()方法
1 //写出文件 2 FileUtils.write(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/happy.text"), "今天要努力学习\r\n", "UTF-8"); 3 FileUtils.writeStringToFile(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/happy.text"), "明天要努力学习\r\n", "UTF-8",true); 4 FileUtils.writeByteArrayToFile(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/happy.text"), "后天要努力学习\r\n".getBytes("UTF-8"),true); 5 6 //写出列表 7 List<String>list = new ArrayList<String>(); 8 list.add("今天学习IO技术"); 9 list.add("明天学习数据库"); 10 list.add("后天写简历"); 11 FileUtils.writeLines(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/happy.text"), "UTF-8", list,"--",true);//true表示使用追加写法
5.复制相关的方法:copyFile(),copyFileToDirectory(),copyDirectoryToDirectory(),copyDirectory(),copyURLToFile(),IOUtils.toString()方法
1 //复制文件 2 FileUtils.copyFile(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/bbbb.jpg"), new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/copy of bbbb.jpg")); 3 //复制文件到目录 4 FileUtils.copyFileToDirectory(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10/bbbb.jpg"), new File("D:")); 5 //复制目录到目录里 6 FileUtils.copyDirectoryToDirectory(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter10"), new File("D:")); 7 //复制目录本身 8 FileUtils.copyDirectory(new File("C:/Users/Dexin/Desktop/java练习/javaexercise/chapter04"), new File("D:/copy of chapter04")); 9 //拷贝URL内容 10 String url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1585907808396&di=412204d0ec99ee7749d1ed9a40205cec&imgtype=0&src=http%3A%2F%2Fpic9.iqiyipic.com%2Fimage%2F20200307%2F48%2Fc6%2Fv_145827302_m_601_m1_480_360.jpg"; 11 FileUtils.copyURLToFile(new URL(url),new File("d:/xiaohudie.jpg")); 12 //拷贝URL内容方法2 13 String datas = IOUtils.toString(new URL("http://www.163.com"),"GBK"); 14 System.out.println(datas);