Java之IO流
生活中,流有水流,河流等,它们是通过水管或者是河渠等管道或通道按一定方向流动形成的。
Java中的IO流和现实中的流很像,也是通过特定的管道,数据按一定的方向流动形成了IO流。这也是我们今天主要讲的内容。
Java中IO流是实现输入,输出的基础,可以方便的实现数据的输入和输出。
Java的IO流中常用到的一些类有,File,InputStream,OutputStream,FileInputStream,FileOutputStream,Reader,Writer,FileReader,FileWriter,BufferedReader,BufferedWriter
1.1 File类
File类在java中表示(带路径的)文件或者目录。
1.1.1 File常用属性和方法
1 public static void main(String[] args) { 2 3 4 5 // 给定路径创建File对象 6 7 // File file = new File("D:"+File.separator+"javatest"+File.separator+"a.txt"); 8 9 File file = new File("d:\\javatest\\b.mp3"); 10 11 System.out.println(file); 12 13 14 15 // 文件基本属性 16 17 System.out.println(file.canExecute()); 18 19 System.out.println(file.canRead()); 20 21 System.out.println(file.canWrite()); 22 23 24 25 // 文件的创建、删除 26 27 if(!file.exists()) { 28 29 30 31 boolean r; 32 33 try { 34 35 r = file.createNewFile(); 36 37 if(r) { 38 39 System.out.println("文件创建成功"); 40 41 } 42 43 } catch (IOException e) { 44 45 e.printStackTrace(); 46 47 } 48 49 } 50 51 52 53 // 删除文件 54 55 file.delete(); 56 57 }
创建文件时会抛出检查时异常IOException
1.1.2 File的路径相关
1 public static void main(String[] args) { 2 3 4 5 File file = new File("d:\\javatest\\a"); 6 7 // File file = new File("a.txt"); 8 9 10 11 // 获取file的绝对路径 12 13 System.out.println(file.getAbsolutePath()); 14 15 // 获取file的创建时的路径字符串 16 17 System.out.println(file.getPath()); 18 19 // 获取文件或者目录的名字 20 21 System.out.println(file.getName()); 22 23 // 获取文件或者目录的父目录 24 25 System.out.println(file.getParent()); 26 27 28 29 }
注意:如果file是相对路径,相对路径的当前路径是工程目录(java17)
1.1.3 目录的创建
1 public static void main(String[] args) { 2 3 4 5 File file = new File("d:\\javatest\\c\\d\\e"); 6 7 8 9 if(!file.exists()) { 10 11 boolean r; 12 13 14 15 try { 16 17 // 一次只能创建一个目录 18 19 // r = file.mkdir(); 20 21 r = file.mkdirs(); 22 23 if(r) { 24 25 System.out.println("目录创建成功"); 26 27 } 28 29 } catch (Exception e) { 30 31 e.printStackTrace(); 32 33 } 34 35 36 37 } 38 39 }
1.1.4 目录的遍历
list():返回一个file表示的目录中的子目录或者文件,字符串数组类型
listFiles():返回一个file表示的目录中的子目录或者文件,File数组类型
1 public static void main(String[] args) { 2 3 4 5 // 需求:遍历d:\javatest目录 6 7 // list() 8 9 File file = new File("d:\\javatest"); 10 11 12 13 14 15 /* 16 17 String[] list = file.list(); 18 19 20 21 for (String str : list) { 22 23 System.out.print(str); 24 25 File f = new File(file.getPath()+"\\"+str); 26 27 if(f.isDirectory()) { 28 29 System.out.println(" 目录"); 30 31 }else { 32 33 System.out.println(" 文件"); 34 35 } 36 37 }*/ 38 39 40 41 42 43 // listFiles(); 44 45 File[] listFiles = file.listFiles(); 46 47 for (File f : listFiles) { 48 49 System.out.print(f.getName()); 50 51 if(f.isDirectory()) { 52 53 System.out.println(" 目录"); 54 55 }else { 56 57 System.out.println(" 文件"); 58 59 } 60 61 } 62 63 }
练习:
1.2 IO流
1.2.1 流
流(stream):流是一连串流动的数据(字节、字符),以先进先出的方式发送的信息的通道中。
1.2.2 输入流和输出流
输入流
数据从源数据源流入程序的过程称为输入流。可以理解为从源数据源读取数据到程序的过程
输出流
数据从程序流出到目的地的过程称为输出流。可以理解为把数据从程序写入目的地的过程
数据源一般指提供数据的原始媒介,一般常见有文件、数据库、云端、其他硬件等能提供数据的媒介。
1.2.3 流的分类
按照流向分为输入流和输出流
按照处理单元分为字节流和字符流
按照功能分为节点流和转换流。
1.3 InputStream/OutputStream
InputStream 是所有字节输入流的抽象父类,提供了
read 读取一个字节
read(byte[] buf) 读取一定量的字节到缓冲区数组 buf中。
OutputStream 是所有字节输出流的抽象父类,提供了
write() 写入一个字节
write(byte[] buf) 写入一定量的字节到输出流
FileInputStream 文件字节输入流,专门用于从文件中读取字节到程序内存中。
FileOutputStream 文件字节输出流,专门用于从内存中写入字节到文件中。
需求:从文件读取一个字节
1 public static void main(String[] args) { 2 3 4 5 // 需求:读取一个文件中的一个字节 6 7 File file = new File("d:\\javatest\\a.txt"); 8 9 10 11 // 【1】创建管道 12 13 FileInputStream in = null; 14 15 16 17 try { 18 19 in = new FileInputStream(file); 20 21 22 23 // 【2】从管道读取一个字节 24 25 /* 26 27 int t; 28 29 t = in.read(); 30 31 t = in.read(); 32 33 t = in.read(); 34 35 t = in.read(); 36 37 */ 38 39 // System.out.println(t); 40 41 42 43 // 循环读取一个字节 44 45 int t; 46 47 StringBuilder sb = new StringBuilder(); 48 49 while( (t=in.read()) != -1 ) { 50 51 sb.append((char)t); 52 53 } 54 55 56 57 System.out.println(sb.toString()); 58 59 60 61 62 63 64 65 } catch (FileNotFoundException e) { 66 67 e.printStackTrace(); 68 69 } catch(IOException e) { 70 71 e.printStackTrace(); 72 73 } 74 75 76 77 // 【3】关闭流管道 78 79 try { 80 81 in.close(); 82 83 } catch (IOException e) { 84 85 e.printStackTrace(); 86 87 } 88 89 }
一次读取多个字节
1 public static void main(String[] args) { 2 3 4 5 // 需求:一次读取多个字节 6 7 File file = new File("d:\\javatest\\a.txt"); 8 9 10 11 // 【1】创建管道 12 13 FileInputStream in = null; 14 15 16 17 try { 18 19 in = new FileInputStream(file); 20 21 22 23 // 【2】从管道读取多个字节到缓冲区 24 25 /* 26 27 byte[] buf = new byte[5]; 28 29 int len; 30 31 len = in.read(buf); 32 33 len = in.read(buf); 34 35 len = in.read(buf); 36 37 len = in.read(buf); 38 39 40 41 for(byte b:buf) { 42 43 System.out.print((char)b+"\t"); 44 45 } 46 47 System.out.println(len); 48 49 */ 50 51 52 53 // 通过循环读取文件 54 55 byte[] buf = new byte[5]; 56 57 int len; 58 59 StringBuilder sb = new StringBuilder(); 60 61 while( (len=in.read(buf)) != -1 ) { 62 63 // 读取的内容是原始二进制流,需要根据编码的字符集解码成对于字符 64 65 String str = new String(buf,0,len); 66 67 sb.append(str); 68 69 } 70 71 System.out.println(sb.toString()); 72 73 74 75 76 77 78 79 80 81 } catch (FileNotFoundException e) { 82 83 e.printStackTrace(); 84 85 } catch(IOException e) { 86 87 e.printStackTrace(); 88 89 } 90 91 92 93 // 【3】关闭流管道 94 95 try { 96 97 in.close(); 98 99 } catch (IOException e) { 100 101 e.printStackTrace(); 102 103 } 104 105 }
需求:按照指定编码写入文件
1 public static void main(String[] args) { 2 3 4 5 6 7 File file = new File("d:\\javatest\\c.txt"); 8 9 10 11 FileOutputStream out = null; 12 13 14 15 try { 16 17 // 【1】创建输出流管道 18 19 out = new FileOutputStream(file); 20 21 22 23 // 【2】写入数据到管道中 24 25 // 一次写入一个字节 26 27 /* 28 29 out.write(97); 30 31 out.write(98); 32 33 out.write(99); 34 35 */ 36 37 38 39 // 一次写入多个字节 40 41 String str = "hello world"; 42 43 // gbk 44 45 /* 46 47 byte[] buf = str.getBytes(); 48 49 out.write(buf); 50 51 */ 52 53 54 55 byte[] buf = str.getBytes("UTF-8"); 56 57 out.write(buf); 58 59 60 61 System.out.println("写入完成!"); 62 63 64 65 } catch (FileNotFoundException e) { 66 67 e.printStackTrace(); 68 69 } catch (IOException e) { 70 71 e.printStackTrace(); 72 73 } 74 75 76 77 // 【3】关闭流 78 79 try { 80 81 out.close(); 82 83 } catch (IOException e) { 84 85 e.printStackTrace(); 86 87 } 88 89 }
注意:
[1]字符串写入文件时一定会存在编码问题
[2]使用utf8编码写入文件时,如果不含中文时,win系统会对文件的编码造成误判。
[3] 通过字节流写入文件时,向管道写入一个字节,该字节立即写入文件中。
总结
InputStream/OutputStream 用于字节的读写。主要用于读取二进制文件(图片、音频、视频),也可以读取文件性文件。
需求:请把d:\\javatest\\logo.png 复制到工程目录中,并显示复制进度。
1 public static void main(String[] args) throws FileNotFoundException,IOException { 2 3 4 5 6 7 File oriFile = new File("d:\\javatest\\logo.jpg"); 8 9 File toFile = new File("logo.jpg"); 10 11 12 13 long totalLen = oriFile.length(); // 文件大小 14 15 long cpyedLen = 0; // 已复制完成的大小 16 17 float progress = 0.0f; 18 19 20 21 FileInputStream in = new FileInputStream(oriFile); 22 23 FileOutputStream out = new FileOutputStream(toFile); 24 25 26 27 // 一次读取1kb 28 29 byte[] buf = new byte[512]; 30 31 int len; 32 33 while( (len=in.read(buf)) != -1) { 34 35 out.write(buf, 0, len); 36 37 cpyedLen += len; 38 39 progress = cpyedLen*1.0f/totalLen; 40 41 System.out.println(progress); 42 43 44 45 46 47 } 48 49 50 51 in.close(); 52 53 out.close(); 54 55 56 57 System.out.println("复制完成!"); 58 59 60 61 }
思考:用字节流来读取文本性文件时,按字节读容易造成乱码。此时,能不能按字符来读取。
1.4 Reader/Writer
Reader 是字符输入流的抽象父类,提供了
read 一次读取一个字符
read(char[] cbuf) 一次读取多个字符到字符缓冲区cbuf,返回长度表示读取的字符个数。
Writer 是字符输出流的抽象父类,提供了
write
write(char[] cbuf)
write(string)
FileReader 文件字符输入流,专门用于读取默认字符编码文本性文件。
FileWriter 文件字符输出流,专门用于写入默认字符编码的文本性文件。为了提高效率,FileWriter内部存在一个字节缓冲区,用于对待写入的字符进行统一编码到字节缓冲区,一定要在关闭流之前,调用flush方法刷新缓冲区。
需求:一次读取一个字符/多个字符到cbuf
1 public static void main(String[] args) throws IOException { 2 3 4 5 File file = new File("d:\\javatest\\d.txt"); 6 7 8 9 FileReader reader = new FileReader(file); 10 11 12 13 // 【1】一次读取一个字符 14 15 /* 16 17 int c; 18 19 c = reader.read(); 20 21 c = reader.read(); 22 23 c = reader.read(); 24 25 c = reader.read(); 26 27 c = reader.read(); 28 29 System.out.println((char)c); 30 31 */ 32 33 34 35 // 【2】一次读取多个字符到cbuf中 36 37 /* 38 39 char[] cbuf = new char[2]; 40 41 int len; 42 43 len = reader.read(cbuf); 44 45 len = reader.read(cbuf); 46 47 len = reader.read(cbuf); 48 49 len = reader.read(cbuf); 50 51 System.out.println(Arrays.toString(cbuf)); 52 53 System.out.println(len); 54 55 */ 56 57 58 59 char[] cbuf = new char[2]; 60 61 int len; 62 63 StringBuilder sb = new StringBuilder(); 64 65 while( (len=reader.read(cbuf)) != -1 ) { 66 67 sb.append(cbuf,0,len); 68 69 } 70 71 72 73 System.out.println(sb); 74 75 }
需求:写入字符到文件中
1 public static void main(String[] args) throws IOException { 2 3 4 5 6 7 File file = new File("d:\\javatest\\f.txt"); 8 9 10 11 FileWriter writer = new FileWriter(file); 12 13 14 15 // 【1】一次写入一个字符 16 17 /*writer.write('中'); 18 19 writer.write('国');*/ 20 21 22 23 // 【2】一次写入多个字符 24 25 /*char[] cbuf = {'h','e','l','l','o','中','国'}; 26 27 writer.write(cbuf);*/ 28 29 30 31 // 【3】一次写入一个字符串 32 33 String str = "hello你好"; 34 35 writer.write(str); 36 37 38 39 40 41 // 刷新字节缓冲区 42 43 writer.flush(); 44 45 46 47 // 关闭流通道 48 49 writer.close(); 50 51 52 53 System.out.println("写入完成"); 54 55 }
思考:如何把字符串以utf8或者其他编码写入文件?
1.5 转换流
InputStreamReader 继承于Reader,是字节流通向字符流的桥梁,可以把字节流按照指定编码 解码 成字符流。
OutputStreamWriter 继承于Writer,是字符流通向字节流的桥梁,可以把字符流按照指定的编码 编码 成字节流。
1.5.1 转换流工作原理
需求:写入utf8文件
1 /** 2 3 * 把一个字符串以utf8编码写入文件 4 5 */ 6 7 public class Test01 { 8 9 public static void main(String[] args) throws IOException { 10 11 12 13 14 15 String str = "hello中国"; 16 17 File file = new File("d:\\javatest\\g.txt"); 18 19 20 21 // 【1】创建管道 22 23 FileOutputStream out = new FileOutputStream(file); 24 25 OutputStreamWriter writer = new OutputStreamWriter(out, "utf8"); 26 27 28 29 // 【2】写入管道 30 31 writer.write(str); 32 33 34 35 // 【3】刷新缓冲区 36 37 writer.flush(); 38 39 40 41 // 【4】关闭管道 42 43 out.close(); 44 45 writer.close(); 46 47 48 49 System.out.println("写入完成"); 50 51 } 52 53 }
需求:读取utf8文件
1 /** 2 3 * 读取utf8编码的文本文件 4 5 */ 6 7 public class Test01 { 8 9 public static void main(String[] args) throws IOException { 10 11 12 13 File file = new File("d:\\javatest\\g.txt"); 14 15 16 17 // 【1】建立管道 18 19 FileInputStream in = new FileInputStream(file); 20 21 InputStreamReader reader = new InputStreamReader(in, "UTF-8"); 22 23 24 25 char[] cbuf = new char[2]; 26 27 int len; 28 29 30 31 StringBuilder sb = new StringBuilder(); 32 33 while( (len=reader.read(cbuf))!=-1 ) { 34 35 sb.append(cbuf, 0, len); 36 37 } 38 39 System.out.println(sb.toString()); 40 41 42 43 } 44 45 }
注意:
[1]win平台默认的utf8编码的文本性文件带有BOM,java转换流写入的utf8文件不带BOM。所以用java读取手动创建的utf8文件会出现一点乱码(?hello中国,?是bom导致的)
[2] 一句话:用字符集编码,一定用字符集解码!!
思考:
FileReader = InputStreamReader + GBK
1 package cn.sxt07.outputstreamwriter; 2 3 4 5 import java.io.File; 6 7 import java.io.FileInputStream; 8 9 import java.io.FileReader; 10 11 import java.io.IOException; 12 13 import java.io.InputStreamReader; 14 15 16 17 /** 18 19 * 读取一个gbk编码的文本性文件 20 21 */ 22 23 public class Test02 { 24 25 public static void main(String[] args) throws IOException { 26 27 28 29 30 31 File file = new File("d:\\javatest\\f.txt"); 32 33 34 35 // 【1】建立管道 36 37 /* 38 39 * FileInputStream in = new FileInputStream(file); 40 41 * InputStreamReader reader = new InputStreamReader(in, "GBK"); 42 43 */ 44 45 46 47 FileReader reader = new FileReader(file); 48 49 50 51 char[] cbuf = new char[2]; 52 53 int len; 54 55 56 57 StringBuilder sb = new StringBuilder(); 58 59 while( (len=reader.read(cbuf))!=-1 ) { 60 61 sb.append(cbuf, 0, len); 62 63 } 64 65 66 67 reader.close(); 68 69 70 71 System.out.println(sb.toString()); 72 73 } 74 75 } 76 77
1.6 BufferedReader/BufferedWriter
BufferedReader 继承于Reader,提供了
read
read(char[] cbuf)
readLine() 用于读取一行文本,实现对文本的高效读取。
BufferedReader 初始化时需要一个reader,本质上BufferedReader在reader的基础上增加readLine()的功能。
BufferedWriter继承于Writer,提供了
write
write(char[] cbuf)
write(string)
newline() 写入一个行分隔符。
需求:读取一首诗
1 public static void main(String[] args) throws IOException { 2 3 4 5 // 按行读取gbk文本性文件 6 7 8 9 File file = new File("d:\\javatest\\i.txt"); 10 11 12 13 // 【1】创建管道 14 15 FileReader reader = new FileReader(file); 16 17 BufferedReader br = new BufferedReader(reader); 18 19 20 21 // 【2】读取一行 22 23 /* 24 25 String line = br.readLine(); 26 27 line = br.readLine(); 28 29 line = br.readLine(); 30 31 line = br.readLine(); 32 33 */ 34 35 36 37 String line; 38 39 while( (line=br.readLine()) != null) { 40 41 System.out.println(line); 42 43 } 44 45 }
需求:以gbk编码写入一首诗到文件
1 public static void main(String[] args) throws IOException { 2 3 4 5 File file = new File("d:\\javatest\\j.txt"); 6 7 8 9 // 【1】创建gbk管道 10 11 FileWriter writer = new FileWriter(file); 12 13 BufferedWriter bw = new BufferedWriter(writer); 14 15 16 17 // 【2】写入一行 18 19 bw.write("窗前明月光,"); 20 21 bw.newLine(); 22 23 24 25 bw.write("疑似地上霜。"); 26 27 28 29 // for win 30 31 // bw.write("\r\n"); 32 33 34 35 // for unix/linux/mac 36 37 // bw.write("\n"); 38 39 40 41 bw.write("举头望明月,"); 42 43 bw.newLine(); 44 45 46 47 // 【3】flush 48 49 bw.flush(); 50 51 52 53 // 【4】关闭管道 54 55 bw.close(); 56 57 writer.close(); 58 59 }
需求:以utf8编码高效写入文件
1 /** 2 3 * 以utf8写入一首诗 4 5 * @author Administrator 6 7 * 8 9 */ 10 11 public class Test02 { 12 13 public static void main(String[] args) throws IOException { 14 15 16 17 File file = new File("d:\\javatest\\j-utf8.txt"); 18 19 20 21 // 【1】创建utf8管道 22 23 FileOutputStream out = new FileOutputStream(file); 24 25 OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8"); 26 27 BufferedWriter bw = new BufferedWriter(writer); 28 29 30 31 // 【2】写入一行 32 33 bw.write("窗前明月光,"); 34 35 bw.newLine(); 36 37 38 39 bw.write("疑似地上霜。"); 40 41 42 43 // for win 44 45 bw.write("\r\n"); 46 47 48 49 // for unix/linux/mac 50 51 // bw.write("\n"); 52 53 54 55 bw.write("举头望明月,"); 56 57 bw.newLine(); 58 59 60 61 // 【3】flush 62 63 bw.flush(); 64 65 66 67 // 【4】关闭管道 68 69 bw.close(); 70 71 writer.close(); 72 73 } 74 75 }