Java基础(八)——IO流2_缓冲流、转换流


一、缓冲流

1、介绍

  缓冲流:不能直接作用在文件上,需要包一层,它是一种处理流。用于提高文件的读写效率。它在流的基础上对流的功能进行了增强。提高读写速度的原因:内部提供了一个缓冲区。缺省使用 8192 个字节(8Kb)的缓冲区 。
  源码示例:BufferedInputStream

1 public class BufferedInputStream extends FilterInputStream {
2     // 默认缓冲区的大小
3     private static int DEFAULT_BUFFER_SIZE = 8192;
4 }

  要求:先关闭外层的流,再关闭内层的流。而关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,可以省略。

2、BufferedInputStream,BufferedOutputStream(字节流的缓冲区)

  代码示例:用字节流缓冲区处理非文本文件(图片,视频等)。缓冲流复制图片。

 1 // 文件:F:\\hello.jpg
 2 public class Main {
 3 
 4     public static void main(String[] args) {
 5         try (// 造节点流
 6              FileInputStream fis = new FileInputStream((new File("F:\\hello.jpg")));
 7              FileOutputStream fos = new FileOutputStream(new File("F:\\hello_1.jpg"));
 8              // 造缓冲流
 9              BufferedInputStream bis = new BufferedInputStream(fis);
10              BufferedOutputStream bos = new BufferedOutputStream(fos);) {
11 
12             byte[] buffer = new byte[1024];
13             int len;
14             while ((len = bis.read(buffer)) != -1) {
15                 bos.write(buffer, 0, len);
16             }
17 
18             // 刷新缓冲区.必须
19             // bos.flush();
20         } catch (Exception e) {
21         }
22     }
23 }
24 
25 // 用于copy 3.64G 的文件花费 18.925s

3、BufferedReader、BufferedWriter(字符流的缓冲区)

  BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者使用默认的大小。在大多数情况下,默认值就足够大了。
  字符读取流缓冲区,该缓冲区提供了一个一次读一行的方法readLine(),方便于对文本数据的获取。当返回null时,表示读到文件末尾。
  BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
  该缓冲区中提供了一个跨平台的换行符:newLine()。
  代码示例:用字符流缓冲区处理文本文件。缓冲流复制文本文件。

 1 // 文件:F:\\hello.txt
 2 // 内容:任意
 3 
 4 // 方式一:会自动换行
 5 public class Main {
 6 
 7     public static void main(String[] args) {
 8         try ( // 造节点流
 9               FileReader fr = new FileReader(new File("F:\\hello.txt"));
10               FileWriter fw = new FileWriter(new File("F:\\hello_1.txt"));
11 
12               // 造缓冲流
13               BufferedReader br = new BufferedReader(fr);
14               BufferedWriter bw = new BufferedWriter(fw);) {
15 
16             // 使用char[]数组
17             char[] cbuf = new char[1024];
18             int len;
19             while ((len = br.read(cbuf)) != -1) {
20                 bw.write(cbuf, 0, len);
21             }
22 
23             // bw.flush();
24         } catch (Exception e) {
25         }
26     }
27 }
28 
29 
30 // 方式二:不会自动换行
31 public class Main {
32 
33     public static void main(String[] args) {
34         try ( // 造节点流
35               FileReader fr = new FileReader(new File("F:\\hello.txt"));
36               FileWriter fw = new FileWriter(new File("F:\\hello_1.txt"));
37 
38               // 造缓冲流
39               BufferedReader br = new BufferedReader(fr);
40               BufferedWriter bw = new BufferedWriter(fw);) {
41 
42             // 使用readLine(),一次性读一行
43             String data;
44             while ((data = br.readLine()) != null) {
45                 // data中不包含换行符,读一行写一行,不会自动换行.若想换行,可以:
46                 // 方法一:
47                 // bw.write(data + "\n");
48 
49                 // 方法二:
50                 bw.write(data);
51                 bw.newLine();
52             }
53 
54             // bw.flush();
55         } catch (Exception e) {
56         }
57     }
58 }

4、装饰设计模式

  自定义的字符读取流缓冲区myReadLine(),将readLine()方法实现了一次。
  代码示例:

 1 class MyBufferedReader {
 2     private final FileReader fileReader;
 3 
 4     public MyBufferedReader(FileReader fileReader) {
 5         this.fileReader = fileReader;
 6     }
 7 
 8     public String myReadLine() throws IOException {
 9         StringBuilder builder = new StringBuilder();
10 
11         int len;
12         while ((len = fileReader.read()) != -1) {
13             if (len == '\r') {
14                 continue;
15             }
16             if (len == '\n') {
17                 return builder.toString();
18             }
19 
20             builder.append((char) len);
21         }
22 
23         if (builder.length() != 0) {
24             return builder.toString();
25         }
26 
27         return null;
28     }
29 
30     public void myClose() {
31         try {
32             fileReader.close();
33         } catch (IOException e) {
34             e.printStackTrace();
35         }
36     }
37 }
38 
39 // 注意关闭资源
40 // 文件:F:\\hello1.txt
41 // 内容:
42 我有a dream!
43 you need to have a dream!我有a dream!
44 you need to have a dream!
45 public class Main {
46     public static void main(String[] args) throws Exception {
47         MyBufferedReader reader = new MyBufferedReader(new FileReader(new File("F:\\hello1.txt")));
48 
49         String temp;
50         while ((temp = reader.myReadLine()) != null) {
51             System.out.println(temp);
52         }
53 
54         reader.myClose();
55     }
56 }
57 
58 // 结果.打印正常

二、转换流

1、介绍

  转换流提供了在字节流和字符流之间的转换。
  InputStreamReader:将InputStream转换为Reader,实现将字节的输入流按指定字符集转换为字符的输入流。解码。
  OutputStreamWriter:将Writer转换为OutputStream,实现将字符的输出流按指定字符集转换为字节的输出流。编码。
  很多时候使用转换流来处理文件乱码问题。实现编和解码的功能。

2、InputStreamReader

  每次调用InputStreamReader中的一个read()方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。
  为了达到最高效率,可要考虑在InputStreamReader外套接一层BufferedReader。
  键盘录入的标准写法:BufferedReader in = new BufferedReader(new InputStreamReader(System.in))
  代码示例:读文件。字节流->字符流

 1 public class Main {
 2 
 3     public static void main(String[] args) {
 4         try (FileInputStream fis = new FileInputStream(new File("F:\\hello.txt"));
 5              InputStreamReader isr = new InputStreamReader(fis, "UTF-8");) {
 6 
 7             char[] cbuf = new char[1024];
 8             int len;
 9             while ((len = isr.read(cbuf)) != -1) {
10                 String str = new String(cbuf, 0, len);
11                 System.out.print(str);
12             }
13 
14         } catch (Exception e) {
15         }
16     }
17 }

  代码示例:读键盘。缓冲流

 1 public class Main {
 2 
 3     public static void main(String[] args) {
 4         try (// 从标准键盘输入.
 5              BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));) {
 6 
 7             String line = null;
 8             while ((line = reader.readLine()) != null) {
 9                 if ("over".equals(line)) {
10                     return;
11                 }
12                 System.out.println(line);
13             }
14         } catch (Exception e) {
15         }
16     }
17 }

3、OutputStreamWriter

  每次调用write()方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对数用途来说已足够大。注意,传递给write()方法的字符没有缓冲。
  为了获得最高效率,可考虑在OutputStreamWriter外套接一层BufferedWriter。以避免频繁调用转换器。例:
  Writer out = new BufferedWriter(new OutputStreamWriter(System.out))
  代码示例:写文件。字符流->字节流

 1 public class Main {
 2 
 3     public static void main(String[] args) {
 4         try (FileInputStream fis = new FileInputStream(new File("F:\\hello.txt"));
 5              FileOutputStream fos = new FileOutputStream(new File("F:\\hello_1.txt"));
 6 
 7              InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
 8              OutputStreamWriter osw = new OutputStreamWriter(fos, "gbk");) {
 9 
10             char[] cbuf = new char[1024];
11             int len;
12             while ((len = isr.read(cbuf)) != -1) {
13                 osw.write(cbuf, 0, len);
14             }
15 
16             isr.close();
17             osw.close();
18         } catch (Exception e) {
19         }
20     }
21 }

  代码示例:读\写键盘。缓冲流

 1 public class Main {
 2 
 3     public static void main(String[] args) {
 4         try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
 5              BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));) {
 6 
 7             String line = null;
 8             while ((line = reader.readLine()) != null) {
 9                 if ("over".equalsIgnoreCase(line)) {
10                     break;
11                 }
12                 writer.write(line.toUpperCase());
13                 writer.newLine(); // 换行
14                 writer.flush();
15             }
16 
17             reader.close();
18             writer.close();
19         } catch (Exception e) {
20         }
21     }
22 }

4、字符集

  ASCII:美国标准信息交换码。用一个字节的7位可以表示。
  ISO8859-1:拉丁码表。欧洲码表。用一个字节的8位表示。
  GB2312:中国的中文编码表。最多两个字节编码所有字符
  GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
  Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
  UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。