• Java IO4:字符流进阶及BufferedWriter、BufferedReader


    字符流和字节流的区别

    拿一下上一篇文章的例子:

    复制代码
     1 public static void main(String[] args) throws Exception
     2 {
     3     File file = new File("D:/writer.txt");
     4     Writer out = new FileWriter(file);
     5     // 声明一个String类型对象
     6     String str = "Hello World!!!";
     7     out.write(str);
     8     out.close();
     9         
    10     // 读文件操作
    11     Reader in = new FileReader(file);
    12     // 开辟一个空间用于接收文件读进来的数据
    13     char c0[] = new char[1024];
    14     int i = 0;
    15     // 将c0的引用传递到read()方法之中,同时此方法返回读入数据的个数
    16     i = in.read(c0);
    17     in.close();
    18         
    19     if (-1 == i)
    20     {
    21         System.out.println("文件中无数据");
    22     }
    23     else
    24     {
    25         System.out.println(new String(c0, 0, i));
    26     }
    27 }
    复制代码

    第8行"out.close()"注释掉可以看一下效果,"writer.txt"一定是空的,控制台上输出的是"文件中无数据",说明一下原因。

    字符流和字节流非常相似,但也有区别,从网上找了一张图:

    从图上看,字符流和字节流最大的区别在于,字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流操作时使用了缓冲区,通过缓冲区再操作文件。这也解释了上面程序的那个问题,为什么不对资源进行close()就无法写入文件的原因。因为在关闭字符流时会强制性地将缓冲区中的内容进行输出,但是如果没有关闭,缓冲区中的内容是无法输出的

    什么是缓冲区?简单理解,缓冲区就是一块特殊的内存区域。为什么要使用缓冲区?因为如果一个程序频繁操作一个资源(文件或数据库),则性能会很低,为了提升性能,就可以将一部分数据暂时读入到内存的一块区域之中,以后直接从此区域读取数据即可,因为读取内存的速度要快于读取磁盘中文件内容的速度。

    在字符流的操作中,所有的字符都是在内存中形成的,在输出前会将所有的内容暂时保存在内存之中,所以使用了缓冲区。

    如果不想在关闭时再输出字符流的内容也行,使用Writer的flush()方法就可以了。

    字符流的原理

    Java支持字符流和字节流,字符流本身就是一种特殊的字节流,之所以要专门有字符流,是因为Java中有大量对于字符的操作,所以专门有字符流。字节流和字符流的转换是以InputStreamReader和OutputStreamWriter为媒介的,InputStreamReader可以将一个字节流中的字节解码成字符,OutputStreamWriter可以将写入的字符编码成自节后写入一个字节流。

    InputStreamReader中的解码字节,是由StreamDecoder完成的,StreamDecoder是Reader的实现类,定义在InputStreamReader的开头:

    public class InputStreamReader extends Reader {
    
        private final StreamDecoder sd;

    同样,OutputStreadWriter中的编码字节,是由StreamEncoder完成的,StreamEncoder是Writer的实现类,定义在OutputStreamWriter的开头:

    public class OutputStreamWriter extends Writer {
    
        private final StreamEncoder se;

    假如不对StreamDecoder和StreamEncoder指定Charset编码格式,将使用本地环境中的默认字符集,例如中文环境中将使用GBK编码。

    InputStreamReader有两个主要的构造函数:

    1、InputStreamReader(InputStream in)

    2、InputStreamReader(InputStream in, String charsetName)

    OutputStreamWriter也有两个主要的构造函数:

    1、OutputStreamWriter(OutputStream out)

    2、OutputStreamWriter(OutputStream out, String charsetName)

    从构造函数就可以看出,字符流是利用字节流实现的。InputStreamReader和OutputStreamWriter的两个构造函数的区别在于,一个是使用的默认字符集,一个可以指定字符集名称。其实FileReader和FileWriter可以看一下源码,很简单,只有构造函数,里面都是分别根据传入的文件绝对路径或者传入的File实例,new出FileInputStream和FileOutputStream,在调用InputStreamReader和OutputStreamWriter的构造方法。这么做,帮助开发者省去了实例化FileInputStream和FileOutputStream的过程,让开发者可以直接以fileName或file作为构造函数的参数

    BufferedWriter、BufferedReader

    为了达到最高的效率,避免频繁地进行字符与字节之间的相互转换,最好不要直接使用FileReader和FileWriter这两个类进行读写,而使用BufferedWriter包装OutputStreamWriter,使用BufferedReader包装InputStreamReader。同样,在D盘下没有"buffered"这个文件,代码示例为:

    File fe = new File("F:\buffer\buff\buffer.txt");
    if(!fe.getParentFile().exists()){
    fe.getParentFile().mkdirs();
    }
    if(fe.getParentFile().exists() && !fe.exists()){
    fe.createNewFile();
    }

    File fe2 = new File("F:\buffer2.txt");
    Writer out = new FileWriter(fe);
    BufferedWriter bw = new BufferedWriter(out);
    bw.write("张三,");
    bw.newLine();
    bw.write("李四,");
    bw.close();
    out.close();

    if(fe.exists() && fe.getName().equals("buffer.txt")){
    Reader in = new FileReader(fe);
    BufferedReader br = new BufferedReader(in);
    String str = null;
    out = new FileWriter(fe2);
    bw = new BufferedWriter(out);
    while ((str = br.readLine()) != null) {
    bw.write(str);
    bw.newLine();
    System.out.println(str);
    }
    bw.flush();
    bw.close();
    out.close();
    br.close();
    in.close();
    }

    没什么问题,输出了文件中的内容。注意两点:

    1、利用BufferedWriter进行写操作,写入的内容会放在缓冲区内,直到遇到close()、flush()的时候才会将内容一次性写入文件。另外注意close()的顺序,一定要先关闭BufferedWriter,再关闭Writer,不可以倒过来,因为BufferedWriter的写操作是通过Writer的write方法写的,如果先关闭Writer的话,就无法将缓冲区内的数据写入文件了,会抛出异常

    2、利用BufferedReader进行读操作,不可以用父类Reader指向它,因为readLine()这个方法是BufferedReader独有的,readLine()的作用是逐行读取文件中的内容

  • 相关阅读:
    关于32位操作系统和64位操作系统对InstallShield打包的影响
    NEWS: Symantec宣布Wise Package Studio将终止
    InstallShield 2012新功能试用(2) 调用MsiGetProperty等MSI API发生变化
    Basic INFO 在命令行Build InstallShield安装包工程获得压缩安装包
    NEWS InstallShield 2012 Service Pack 1发布
    Basic INFO InstallShield Basic MSI工程中如何在SetupCompleteSuccess界面中启动Readme
    Basic INFO InstallShield的脚本编辑器中如何显示代码行号
    Basic INFO 关于在InstallShield制作的安装包界面中删除InstallShield文字的厂商回复
    Basic INFO InstallShield工程中如何让产品的快捷方式名称始终与产品名保持一致
    Basic INFO: 创建隐藏文件夹
  • 原文地址:https://www.cnblogs.com/qypx520/p/5736014.html
Copyright © 2020-2023  润新知