IO流
IO流体系:
字节流
-
这两个为抽象类
-
inputStream 字节输入流 负责读取数据
-
FileInputStream 操作本地文件 字节输入流
-
-
OutputStream 字节输出流 负责写出数据
-
FileOutputStream 操作本地文件的字节输出流
-
把程序中的数据写到本地文件中
-
创建输出流对象 写数据 释放资源
-
FileOutputStream fileOutputStream = new FileOutputStream("文件目录"); //写入数据 fileOutputStream.write(97); //释放资源 fileOutputStream.close();
-
创建对象 细节1:参数是字符串表示的路径或者是File对象都是可以的 细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径的存在 细节3:如果文件已经存在会清空文件写出数据 细节1:write方法中的参数是整数,实际写入文件的数据是ASCII码中对应的字符释放资源 每次使用完必须释放资源
-
一次性写多个,把数据放入Byte数组里就行
-
//一次性写入多个 byte[] bytes ={97,98,99,100,101}; fileOutputStream.write(bytes); fileOutputStream.write(bytes,0,3);//0 起始索引 3 个数
-
换行和续写
-
// 换行写 再次写出一个换行符 /* 换行符 Windows :\r\n Linux: \n Mac: \r 细节:在java中Windows只需要写一个\r或者\n就可以 因为底层会补全 建议写全 */ String str = "qwertyjkl"; byte[] bytes1 = str.getBytes();//把字符串转为字符数组 fileOutputStream.write(bytes1); String wrap = "\r\n"; byte[] wrapBytes = wrap.getBytes(); fileOutputStream.write(wrapBytes); String str2 = "666"; byte[] bytes2 = str2.getBytes();//把字符串转为字符数组 fileOutputStream.write(bytes2); // 续写 打开续写开关就行 位置在创建对象的第二个参数 默认为false //FileOutputStream fileOutputStream = new FileOutputStream("",true);
-
-
FileInputStream 操作本地文件的字节输入流
-
步骤 创建字节输入流对象 读数据 释放资源
-
/* 演示 字符输入流 FileInputStream 读取文件中的数据 步骤: 创建对象 细节:如果文件不存在直接报错 读取数据 细节:一次读一个字节 读出来是ASCII码上对应的数字 细节:读到文件末尾 read方法返回-1 如果是空格不是文件末尾 是32 释放资源 */ FileInputStream fileInputStream = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo01\\a.txt"); int read = fileInputStream.read();//只读取了第一个数据 返回其ASICC码 System.out.println(read); int read2 = fileInputStream.read();//只读取了第二个数据 返回其ASICC码 System.out.println((char) read2);//强转输出原先字符 如果读取不到 就返回-1 //循环读取 int b; //read 读取一次 移动一次指针 while ((b=fileInputStream.read())!=-1){ System.out.print((char) b); } fileInputStream.close();
-
-
文件拷贝
-
/* 文件拷贝 */ FileInputStream inputStream = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo01\\a.txt"); FileOutputStream outputStream = new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo03\\aa.txt"); //拷贝 核心 边读边写 int b; while ((b = inputStream.read())!=-1){ outputStream.write(b); } outputStream.close(); inputStream.close();//先开后关
-
-
文件拷贝使用字节数组
-
FileInputStream inputStream = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo01\\a.txt"); FileOutputStream outputStream = new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo03\\aaa.txt"); //拷贝 int len; byte[] bytes = new byte[1024]; while ((len = inputStream.read(bytes))!=-1){ outputStream.write(bytes,0,len); } outputStream.close(); inputStream.close();
-
-
文件拷贝使用try catch 去抛出异常
-
package IOLearn.Stream.demo03; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ByteStreamDemo04 { public static void main(String[] args) throws Exception { /* 文件拷贝 利用try catch finally 捕获异常 */ FileInputStream inputStream = null; FileOutputStream outputStream = null; try { inputStream = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo01\\a.txt"); outputStream = new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo03\\aaa.txt"); //拷贝 int len; byte[] bytes = new byte[1024]; while ((len = inputStream.read(bytes))!=-1){ outputStream.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //释放资源 //优化 实现AutoCloseable接口 特定情况下可以自动释放接口 if (outputStream!=null){ try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (inputStream!=null){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
-
-
字符集
-
ASCII:存储英文和符号 一共128个 从0到127
-
GBK:Windows系统默认使用GBK
-
GBK完全兼容ASCII
-
英文一个字节存储编码规则 不足8位 前面补0
-
汉字使用两个字节存储 第一个字节为高位字节 第二个为低位字节
-
高位字节二进制一定以1开头 转成十进制后是一个负数 编码规则不需要变动 和英文进行区别
-
-
Unicode:国际标准字符集 将世界各种语言的每一个字符定义唯一的编码
-
UTF-16 就是转成16个比特位 用2-4个字节保存
-
UTF-32 固定使用四个字节
-
UTF-8 用1-4个字节保存 实际开发常见
-
ASCII 一个字节 不足补0
-
简体中文 3个字节 1110xxxx 10xxxxxx 10xxxxxx 编码规则 x 用汉字对应Unicode的二进制填充
-
-
乱码原因
-
读取数据未读完整个汉字
-
编码和解码方式不统一
-
字节流读取中文会乱码 为什么拷贝文本文件不乱码
-
因为拷贝是一个一个字节拷贝过去 数据没有丢失 只要记事本读取时字符集和编码方式与数据源一致就不会有乱码
-
java中编码和解码方法
编码
getBytes()//默认的方式编码 getBytes(String charsetName)//使用指定的方法编码
idea默认为UTF-8 eclipse默认为GBK
解码
String(byte[] bytes)//默认方式解码 String(byte[] bytes,String charsetName)//使用指定方式进行解码
/* 编码 解码 */ //编码 String str = "ai你嘿嘿"; byte[] bytes = str.getBytes();//idea默认为UTF-8 System.out.println(Arrays.toString(bytes));//[97, 105, -28, -67, -96, -27, -104, -65, -27, -104, -65] 编码后的字节数据 中文三个 英文一个00 byte[] gbks = str.getBytes("GBK");//指定使用GBK编码 System.out.println(Arrays.toString(gbks));//[97, 105, -60, -29, -70, -39, -70, -39] 编码后字节数据 英文一个字节 中文两个字节 //解码 String str2 = new String(bytes); System.out.println(str2); String gbk = new String(gbks, "GBK");//编码解码应规则一致 如果这里解bytes就会乱码ai浣犲樋鍢� System.out.println(gbk); }
字符流
字符流底层就是字节流 + 字符集
-
这两个为抽象类
-
Reader 字符输入流 一次读一个字节,遇到中文 一次读多个字节 读到文件末尾返回-1
-
使用子类FileReader 操作本地文件
-
空参read方法:
-
/* 字符输入流 */ FileReader fr = new FileReader("javaselearn\\src\\IOLearn\\Stream\\mycharset\\a.txt"); //读取数据 字符流底层就是字节流 默认一个一个字节读 //如果遇到中文就一次读取多个字节 /* read细节: 默认一个一个读,中文多个 读取后 方法底层进行解码返回十进制 */ int ch; while ((ch=fr.read())!=-1){ System.out.print((char) ch); } fr.close();
-
有参read方法:
-
/* 字符输入流 */ FileReader fr = new FileReader("javaselearn\\src\\IOLearn\\Stream\\mycharset\\a.txt"); //读取数据 字符流底层就是字节流 默认一个一个字节读 //如果遇到中文就一次读取多个字节 /* read细节: 默认一个一个读,中文多个 读取后 方法底层进行解码返回十进制 看见中文需要自己强转 有参的read方法 把读取数据,解码,强转 三步合并了,把强转之后的字符放数组里了 相当于空参read方法+强制类型转换 */ char[] chars = new char[2]; int len; while ((len=fr.read(chars))!=-1){ System.out.print(new String(chars,0,len)); } fr.close();
-
-
Write 字符输出流 底层会把数据按照指定的编码方式进行编码,变成字节在写入文件
-
使用子类FileWrite 操作本地文件 可以续写,默认为false
-
/* 字符输出流 创建对象 可以开启是否续写 默认false 读取数据 可以写出一个字符 字符串 字符数组等 */ FileWriter fw = new FileWriter("javaselearn\\src\\IOLearn\\Stream\\mycharstream01\\a.txt",true); // fw.write(25105); /* 25105 我 如果使用字节流写入会乱码 因为字节流是一个一个字节写 字符流会先按照编码规则 在写入文件 */ // fw.write("你好啊"); char[] chars = {'a','b','v'}; fw.write(chars); fw.close();
-
-
适合操作纯文本文件
-
字符流原理解析:
-
在内存中有一个8192的字节数组缓存区 提高效率 先从数据源读取数据尽可能填满缓冲区 如果缓冲区有数据则从缓存区中读取 缓存区中没有就区数据源读取数据尽可能填满缓冲区
-
补充:
-
FileReader fr = new FileReader("javaselearn\\src\\IOLearn\\Stream\\mycharstream01\\a.txt"); fr.read(); FileWriter fw = new FileWriter("javaselearn\\src\\IOLearn\\Stream\\mycharstream01\\a.txt"); //因为没开续写会清空文件 //如果再次使用fr读取会有数据吗? //会有数据 因为字符流读取会先存到缓存区中 然后在从缓存区中读取 虽然文件里面的数据清空了都是缓冲区还有数据 只能读取缓存区中 超过缓存区的无法读取 int ch; while ((ch= fr.read())!=-1){ System.out.println((char) ch); } fw.close(); fr.close();
-
什么时候缓存区中的数据才真正写入目的地?
-
缓冲区装满了
-
flush刷新 还可以继续写
-
close释放资源 不能继续写
-
-
使用场景:
-
字节流 拷贝任意类型的文件
-
字符流 读取存文本文件中的数据 往纯文本文件中写出数据
-
练习一:拷贝一个文件夹 考虑子文件夹
-
package IOLearn.Stream.mytest; import java.io.*; public class Test01 { public static void main(String[] args) throws IOException { /* 拷贝一个文件夹 考虑子文件夹 */ //1创建对象表示数据源 File src = new File("E:\\test\\bb"); //2创建对象目的地 File dest = new File("E:\\test\\cc"); //3调用方法开始拷贝 copydir(src,dest); } /* 作用拷贝文件夹 file是数据源 dest是目的地 */ private static void copydir(File src,File dest) throws IOException { //dest可能不存在 直接创建出来 有了创建失败 不会报错 dest.mkdir(); //递归 //1.进入数据源 File[] files = src.listFiles(); //2.遍历数组 for (File file1 : files) { if (file1.isFile()){ //3.判断文件 拷贝 文件开始 文件结束 FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(new File(dest,file1.getName()));//不能直接dest 因为dest这是文件夹 不是文件 byte[] bytes = new byte[1024]; int len; while ((len=fis.read(bytes))!=-1){ fos.write(bytes,0,len); } fos.close(); fis.close(); }else { //4.判断文件夹 递归 copydir(file1,new File(dest,file1.getName())); } } } }
-
练习二:文件加密
-
加密原理:对原始文件中的每一个字节数据进行更改,将更改以后的数据存储到新的文件
-
解密原理:读取加密后的文件,按照加密的规则反向操作,变成原始文件
-
-
/* 文件加密 ^ :异或操作 两边相同结果 false 两边不同 true 如果两边是数字 把数字变为二进制 进行计算 相同为0 不同为1 100^10 ->110 110^10 ->100 */ //创建对象关联原始文件 // FileInputStream fis = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\1.jpg");//没有加密的原始文件 FileInputStream fis = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\11.jpg");//加密了的文件 //创建对象关联加密文件 FileOutputStream fos = new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\111.jpg"); //加密处理 int b; while ((b=fis.read())!=-1){ fos.write(b^2); } fos.close(); fis.close();
-
练习三:修改文件中的数据
-
/* 修改文件中数据 有文本文件 2-1-9-4-7-8 将其排序变为 1-2-4-7-8-9 */ //1.读取数据 FileReader reader = new FileReader("a.txt"); StringBuilder sb = new StringBuilder(); int ch; while ((ch=reader.read())!=-1){ sb.append((char) ch); } reader.close(); System.out.println(sb); //2.排序 String str = sb.toString();//转成字符串 String[] arrstr = str.split("-");//按照-进行切割 ArrayList<Integer> list = new ArrayList<>(); for (String s : arrstr) { int i = Integer.parseInt(s); list.add(i); } System.out.println(list); Collections.sort(list); System.out.println(list); //3.排序结果写出 FileWriter writer = new FileWriter("a.txt"); for (int i = 0; i < list.size(); i++) { if (i==list.size()-1){ writer.write(list.get(i)+"");//加+"" 原因如果不加直接写整数进去会是对应的ASCII码 }else { writer.write(list.get(i)+"-"); } } writer.close(); }
-
非常规写法 stream流和方法引用
-
/* 修改文件中数据 有文本文件 2-1-9-4-7-8 将其排序变为 1-2-4-7-8-9 非常规写法 细节 文件中的数据不要换行 本地文件可能会有一个隐藏的bom头 */ //1.读取数据 FileReader reader = new FileReader("a.txt"); StringBuilder sb = new StringBuilder(); int ch; while ((ch=reader.read())!=-1){ sb.append((char) ch); } reader.close(); System.out.println(sb); //2.排序 //stream流操作 Integer[] arr = Arrays.stream(sb.toString() .split("-")) .map(Integer::parseInt) .sorted() .toArray(Integer[]::new); System.out.println(Arrays.toString(arr));//[1, 2, 4, 7, 8, 9] //3.排序结果写出 FileWriter fw = new FileWriter("a.txt"); String s= Arrays.toString(arr).replace(", ","-");//字符串替换[1-2-4-7-8-9] System.out.println(s); String substring = s.substring(1, s.length() - 1);//字符串截取1-2-4-7-8-9 System.out.println(substring); fw.write(substring); fw.close();
高级:缓冲流 提高效率
为什么可以提高效率:
-
缓冲流自带长度问为8192的缓冲区(字节数组
-
可以显著提高字节流的读写性能
-
对于字符流提升不明显,关键是readLine和nemLine两个特有方法
字节缓冲流
-
BufferedInputStream 字节缓冲输入流 底层自带8192的缓冲区 是对基本流的包装
-
BufferedOutputStream 字节缓冲输出流
-
/* 利用字节缓冲流 拷贝文件 */ //创建缓冲流对象 BufferedInputStream bis = new BufferedInputStream( new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\a.txt")); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\copy.txt")); //读取 int b; while ((b= bis.read())!=-1){ bos.write(b); } bos.close(); bis.close();
-
/* 利用字节缓冲流 拷贝文件 使用byte数组 一次读取多个数据 */ //创建缓冲流对象 BufferedInputStream bis = new BufferedInputStream( new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\a.txt")); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\copy02.txt")); //读取 byte[] bytes = new byte[1024]; int b; while ((b= bis.read(bytes))!=-1){ bos.write(bytes,0,b); } bos.close(); bis.close();
-
原理解析:
-
缓冲输入和输出都分别有一个缓冲区 两个的缓冲区不一样
-
字符缓冲流
-
BufferedReader 字符缓冲输入流 特有方法 readLine 读取一行数据 读完返回null
-
/* 字符缓冲输入流 特有方法: readLine 读取一整行 */ BufferedReader br = new BufferedReader( new FileReader("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\a.txt")); //读取数据 //细节 readLine一次读取一整行 遇到回车换行结束 //但是不会把回车换行 读到内存中 // String line = br.readLine(); // System.out.println(line); // String line2 = br.readLine(); // System.out.println(line2); String line; while ((line= br.readLine())!=null){ System.out.println(line); } // br.close();
-
BufferedWrite 字符缓冲输出流 特有方法 newLine 跨平台的换行
-
/* 字符缓冲输出流 特有方法: newLine 跨平台的换行 开启续写在对象FileWriter中 */ BufferedWriter bw = new BufferedWriter( new FileWriter("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\b.txt",true)); bw.write("海拔比阿扁"); bw.newLine(); bw.write("lv哈哈哈"); bw.close();
练习
拷贝文件时间测试
public static void main(String[] args) throws Exception { /* 四种不同拷贝文件方式测试时间 */ long start = System.currentTimeMillis(); // method01();//75316毫秒 // method02();//22毫秒 // method03();//249毫秒 method04();//20毫秒 long end =System.currentTimeMillis(); System.out.println(end-start+"毫秒"); } //字节流读取 一次读取一个字节 public static void method01() throws Exception{ FileInputStream fis = new FileInputStream("E:\\test\\test.mp4"); FileOutputStream fos = new FileOutputStream("E:\\test\\copy01.mp4"); int b; while ((b=fis.read())!=-1){ fos.write(b); } fos.close(); fis.close(); } //字节流的基本流 一次读取一个字节数组 public static void method02() throws Exception{ FileInputStream fis = new FileInputStream("E:\\test\\test.mp4"); FileOutputStream fos = new FileOutputStream("E:\\test\\copy02.mp4"); byte[] bytes = new byte[8192]; int len; while ((len=fis.read(bytes))!=-1){ fos.write(bytes,0,len); } fos.close(); fis.close(); } //字节流的缓冲流 一次读取一个字节 public static void method03() throws Exception{ BufferedInputStream br = new BufferedInputStream(new FileInputStream("E:\\test\\test.mp4")); BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("E:\\test\\copy03.mp4")); int b; while ((b=br.read())!=-1){ bw.write(b); } bw.close(); br.close(); } //字节流的缓冲流 一次读取字节数组 public static void method04() throws Exception{ BufferedInputStream br = new BufferedInputStream(new FileInputStream("E:\\test\\test.mp4")); BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("E:\\test\\copy04.mp4")); byte[] bytes = new byte[8192]; int len; while ((len=br.read(bytes))!=-1){ bw.write(bytes,0,len); } bw.close(); br.close(); }
恢复诗词顺序
/* 恢复诗词顺序 */ BufferedReader br = new BufferedReader( new FileReader("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\a.txt")); String line; ArrayList<String> list = new ArrayList<>(); while ((line= br.readLine())!=null){ System.out.println(line); list.add(line); } br.close(); //排序 //排序规则 按照序号排列 Collections.sort(list, new Comparator<String>() { @Override public int compare(String o1, String o2) { //获取o1 和 o2 的序号 int i1 = Integer.parseInt(o1.split("\\.")[0]); int i2 = Integer.parseInt(o2.split("\\.")[0]); return i1-i2; } }); System.out.println(list); //写入文件 BufferedWriter bw = new BufferedWriter( new FileWriter("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\sout.txt")); for (String str : list) { bw.write(str); bw.newLine(); } bw.close(); /* 3.这是第三句 5.这是第一句,后面的后面的,就是第五句 1.第一句 4.这是第四句!好 2.这是第二 */
写法2:通过Tree集合 会自动排序 使用TreeMap 把前面的序号作为键,后面的中文作为值
/* 恢复诗词顺序 */ BufferedReader br = new BufferedReader( new FileReader("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\a.txt")); String line; TreeMap<Integer,String> tm =new TreeMap<>();//Tree集合会自动排序 while ((line= br.readLine())!=null){ System.out.println(line); //0. 序号 1:内容 String[] arr = line.split("\\."); tm.put(Integer.parseInt(arr[0]),arr[1]); } br.close(); System.out.println(tm); //写入文件 BufferedWriter bw = new BufferedWriter( new FileWriter("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\sout02.txt")); Set<Map.Entry<Integer, String>> entries = tm.entrySet(); for (Map.Entry<Integer, String> entry : entries) { bw.write(entry.getValue()); bw.newLine(); } bw.close();
验证软件运行次数,运行超过三次收费
/* 验证软件运行次数,运行超过三次收费 把标志存入文件中 */ //1.读取文件数字 BufferedReader br = new BufferedReader( new FileReader("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\count.txt")); String line = br.readLine(); br.close(); int count = Integer.parseInt(line); count++; if (count<=3){ System.out.println("欢迎您第"+count+"次使用本软件"); }else { System.out.println("免费次数以用尽,请充值"); } BufferedWriter bw = new BufferedWriter( new FileWriter("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\count.txt")); bw.write(count+""); bw.close();
IO流使用原则随用随创建,不用立即关闭
高级:转换流
属于字符流 是字符流和字节流之间的桥梁
作用:字节流想要使用字符流中的方法
指定字符集读写数据(JDK11中淘汰
-
转换输入流 InputStreamReader
-
转换输出流OutputStreamWrite
-
利用转换流按照指定字符编码读取 */ /* InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"), "GBK"); //读取 int ch; while ((ch= isr.read())!=-1){ System.out.print((char) ch); } isr.close(); 上面方案了解就行,在JDK11被淘汰了 */ // FileReader fr = new FileReader("gbk.txt", Charset.forName("GBK")); // int ch; // while ((ch= fr.read())!=-1){ // System.out.print((char) ch); // } // fr.close();
-
/* 利用转换流按照指定字符编码写出 不要在idea里面修改文件的字符编码,修改的话可能会有很大问题 */ OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk02.txt"),"GBK"); //写出数据 osw.write("你好你好"); osw.close(); //上面JDK11淘汰了 //JDK11写法 // FileWriter fw = new FileWriter("gbk03.txt", Charset.forName("GBK")); // fw.write("JDK11里面使用"); // fw.close();
-
/* 将本地文件中的GBK文件,转成UTF-8 */ InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"), "GBK"); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8"); int ch; while ((ch=isr.read())!=-1){ osw.write(ch); } osw.close(); isr.close(); //JDK11以前的写法上面 11后可以不用转换流 直接使用new FileReader 里面可以指定编码 //new FileWriter("gbk03.txt", Charset.forName("GBK"))
-
练习2:利用字节流读取文件中数据,一次读一整行,不能乱码
-
/* 利用字节流读取文件中数据,一次读一整行,不能乱码 字节流没有这些功能所以需要把字节流转换成字符流 在包装成字符缓冲流 */ // FileInputStream fis = new FileInputStream("gbk.txt"); // InputStreamReader isr = new InputStreamReader(fis); // BufferedReader br = new BufferedReader(isr); // String str = br.readLine(); // System.out.println(str); // br.close(); BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream("gbk.txt"), "GBK")); String line; while ((line= br.readLine())!=null){ System.out.println(line); } br.close();
高级:序列化流
属于字节流
-
序列化流 输出数据 ObjectOutputStream 把java中的对象写入本地文件
-
如果对象没有实现Serializable接口 进行序列化会报错java.io.NotSerializableExceptionSerializable接口没有抽象方法 是标记型接口
-
/* 利用序列化流/对象操作输出流,把一个对象写入本地文件 如果对象没有实现Serializable 进行序列化会报错 java.io.NotSerializableException Serializable接口没有抽象方法 是标记型接口 */ //创建 Student student = new Student("zhangsna",23); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")); oos.writeObject(student); oos.close();
-
-
反序列化流 输入数据 ObjectInputStream
-
/* 利用反序列化流把本地序列化的对象 读取到程序中 */ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")); Student o = (Student) ois.readObject(); System.out.println(o); ois.close();
-
-
对象实现Serializable接口后会根据对象的信息生成一个版本号,如果对对象进行了修改操作,版本号会改变,这时候如果进行反序列化 两种的版本号不一致就会报错
-
解决:固定版本号 自己写一个固定的版本号 写法固定:
-
private static final long serialVersionUID = 1L;//第一种写法
-
第二种通过IDea设置检查 然后自动生成
-
private transient String address;//如果不想把该属性序列化 添加关键字transient
-
-
序列化流后的文件不能修改 一但修改无法再反序列化流
练习
写入
/* 将多个自定义对象序列化到文件中,但是对象的个数不确定 */ Student s1 = new Student("张三", 21, "南京"); Student s2 = new Student("李四", 20, "北京"); Student s3 = new Student("王五", 23, "深圳"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt")); ArrayList<Student> list = new ArrayList<>(); list.add(s1); list.add(s2); list.add(s3); oos.writeObject(list); oos.close();
读出:
/* 反序列化读取 存了不定量的对象 */ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt")); //读取 // Student o1 = (Student) ois.readObject(); // Student o2 = (Student) ois.readObject(); // Student o3 = (Student) ois.readObject(); // Student o4 = (Student) ois.readObject();//EOFException 读到末尾报异常 // // System.out.println(o1); // System.out.println(o2); // System.out.println(o3); // System.out.println(o4); // 这样读取无法知道是否读完 因此我们要在序列化时把对象放入一个集合 再把集合序列化写入文件 ArrayList<Student> list = (ArrayList<Student>) ois.readObject(); System.out.println(list); ois.close();
高级:打印流
打印流只能写
特点:
-
打印流只能操作文件目的地不能操作数据源
-
特有的写出方法 数据原样写出
-
特有的写出方法 可以实现自动刷新 自动换行
-
字节打印流 PrintStream
-
底层没有缓冲区 开不开自动刷新都一样
-
new 的时候可以传入(OutputStream/File/String)
-
可以指定字符编码
-
write 普通方法
-
println 特有方法 打印任意数据 自动刷新 自动换行
-
print 特有方法 打印任意数据 不换行
-
printf特有方法 带有占位符的打印语句 不换行
-
/* 字节打印流 */ //创建对象 // PrintStream ps = new PrintStream("a.txt");//默认utf-8 PrintStream ps = new PrintStream(new FileOutputStream("a.txt"),true,"UTF-8");//默认utf-8 ps.println(97);//原样写出 文件中写出97 ps.print(true); ps.printf("%s 爱死了 %s","A","B");//占位符 /* %s 字符串的占位符 %n 换行 %c 把字符变成大写 %b 布尔类型 。。。。。 */ ps.close();
-
-
字符打印流 PrintWriter
-
字符打印流底层有缓冲区 自动刷新需要开启
-
构造方法和字节打印流一样 成员方法有一样
-
/* 字符打印流 自动刷新需要自己开启 */ PrintWriter pw = new PrintWriter(new FileWriter("a.txt"),true); pw.println("你好"); pw.print("我不好"); pw.printf("%s 很好","它"); pw.close();
-
-
输出语句和打印流之间的关系
-
//获取打印流的对象,此打印流在虚拟机启动的时候 由虚拟机创建 默认指向控制台//特殊的打印流 系统中的标准输出流 不能被关闭的 在系统中唯一 PrintStream ps = System.out; ps.println("123");
-
高级:压缩流
属于字节流
-
解压缩流
-
在java中压缩包中的每一个文件就是一个ZipEntry对象
-
public static void main(String[] args) throws Exception { /* 解压缩流 */ File src = new File("aaa.zip"); File dest = new File("E:\\"); unZip(src,dest); } public static void unZip(File src,File dest) throws Exception{ //解压本质是把压缩包里面的文件或文件夹读取出来 按照层级拷贝 ZipInputStream zip = new ZipInputStream(new FileInputStream(src)); // ZipEntry entry = zip.getNextEntry(); // System.out.println(entry); // //如果获取完毕 后面返回null // ZipEntry entry1 = zip.getNextEntry(); // System.out.println(entry1); ZipEntry entry; while ((entry= zip.getNextEntry())!=null){ System.out.println(entry); //文件夹 在目的地创建一个同样的文件夹 //文件 按照层级目录存放 if (entry.isDirectory()){ //是文件夹 File file = new File(dest,entry.toString()); file.mkdir(); }else { //目的地: FileOutputStream fos = new FileOutputStream(new File(dest,entry.toString())); int b; while ((b=zip.read())!=-1){ fos.write(b); } fos.close(); zip.closeEntry(); } } zip.close(); }
-
-
压缩流
-
压缩单独一个文件
-
public static void main(String[] args) throws Exception { /* 压缩流 把一个文件压缩 */ //创建对象 File src = new File("E:\\aaa\\aaa.txt"); File dest = new File("E:\\aaa"); toZip(src,dest); } public static void toZip(File src,File dest) throws Exception { ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest,"a.zip"))); //创建ZipEntry对象 表示压缩包里的每一个文件和文件夹 ZipEntry entry = new ZipEntry("aaa.txt"); //把entry放入压缩包 zos.putNextEntry(entry); //上面只能把结构建好 数据还没有写入 FileInputStream fis = new FileInputStream(src); int b; while ((b=fis.read())!=-1){ zos.write(b); } fis.close(); zos.closeEntry(); zos.close(); }
-
// ZipEntry entry = new ZipEntry("bbb\\ccc\\aaa.txt");里面的路径表示压缩包里面的路径
-
压缩文件夹
-
public static void main(String[] args) throws Exception{ /* 压缩流 压缩文件夹 */ //被压缩文件夹 File src = new File("E:\\aaa"); //压缩包被放在哪里 File destParent = src.getParentFile();//D:\\ //创建File对象 表示压缩包路径 File dest = new File(destParent,src.getName()+".zip"); System.out.println(dest); //创建压缩流 ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest)); //获取src中的每一个文件 变成ZipEntry对象 放入压缩包 toZip(src,zos, src.getName()); zos.close(); } /* 获取src中的每一个文件 变成ZipEntry对象 放入压缩包 src 数据源 zos 压缩流 name 内部路径 */ public static void toZip(File src,ZipOutputStream zos,String name) throws Exception { //进入src文件夹 File[] files = src.listFiles(); //遍历数组 for (File file : files) { if (file.isFile()){ //文件 变成ZipEntry对象 放入压缩包中 ZipEntry entry = new ZipEntry(name+"\\"+file.getName());//file 是一条路径 这里name是前面的路径aaa zos.putNextEntry(entry); //读取文件 FileInputStream fis = new FileInputStream(file); int b; while ((b= fis.read())!=-1){ zos.write(b); } fis.close(); zos.closeEntry();//表示当前文件书写完毕 }else { //文件夹 i递归 toZip(file,zos,name+"\\"+file.getName()); } } }
-
Commons-io IO操作的开源工具包
提高io流开发效率
-
FileUtils类 (文件,文件夹相关)
-
IOUtils类 (流相关)
/* FileUtils类 */ // File src = new File("a.txt"); // File dest = new File("copy.txt"); // FileUtils.copyFile(src,dest);//拷贝文件 //拷贝文件夹 File src = new File("E:\\aaa"); File dest = new File("E:\\bbb"); FileUtils.copyDirectory(src,dest);//直接拷贝aaa里面的内容放入bbb FileUtils.copyDirectoryToDirectory(src,dest);//直接把aaa整个放入bbb (在bbb中有了aaa这个文件夹 上面的没有) FileUtils.deleteDirectory(src);//删除整个文件夹 aaa也没有了 FileUtils.cleanDirectory(src);//清空文件夹 aaa里面的内容没有了 aaa还存在
Hutool工具包
/* Hutool工具包 FileUtil类 */ /* File file = FileUtil.file("D:\\", "aaa", "a.txt");//工具参数创建file对象 强大:可以写可变参数 自动拼接 System.out.println(file);//D:\aaa\a.txt File touch = FileUtil.touch(file);//根据参数创建文件 父级不存在 会帮你创建 System.out.println(touch); //写入文件按行 ArrayList<String> list = new ArrayList<>(); list.add("aaa"); list.add("aaa"); list.add("aaa"); File file1 = FileUtil.writeLines(list, "E:\\a.txt", "UTF-8", false);//false 不追加 true追加 System.out.println(file1); */ //追加写入 /* ArrayList<String> list = new ArrayList<>(); list.add("aaa"); list.add("aaa"); File file3 = FileUtil.appendLines(list, "E:\\a.txt", "UTF-8"); System.out.println(file3); */ //把文件读取后 放入集合返回 List<String> list = FileUtil.readLines("E:\\a.txt", "UTF-8"); System.out.println(list);