Java IO之字节流和字符流

1 字节流

  在前面的学习过程中,我们一直都是在操作文件或者文件夹,并没有给文件中写任何数据。现在我们就要开始给文件中写数据,或者读取文件中的数据。

1.1 字节输出流OutputStream

  OutputStream此抽象类,是表示输出字节流的所有类的超类。操作的数据都是字节,定义了输出字节流的基本共性功能方法。
  输出流中定义都是写write方法,如下图:

1.1.1 FileOutputStream类

  OutputStream有很多子类,其中子类FileOutputStream可用来写入数据到文件。FileOutputStream类,即文件输出流,是用于将数据写入 File的输出流。

构造方法

1.1.2 FileOutputStream类写入数据到文件中

将数据写到文件中

package com.qtw.api;

import java.io.FileOutputStream;
import java.io.IOException;

/*
* 字节输出流
*   java.io.OutputStream所有字节输出流的超类
*   作用:从java程序写出文件
*   字节:这样的流每次文件中的1个字节
*   写任意文件
*
*   方法都是写入的方法
*
* write(int b) 写入1个字节
* write(byte[] b) 写入字节数组
* write(byte[] b,int,int) 写入字节数组,int 开始写入的索引,int写几个
* close()方法,关闭流对象,释放此次流相关的资源
*
* 流对象,操作文件的时候,自己不做,依赖操作系统
*
*FileOutputStream
*   写入数据文件,学习父类方法,使用子类对象
*
*   子类的构造方法:作用:绑定输出的输出源
*       参数
*           File   文件对象
*           String   字符串的文件名
*   流对象使用步骤:
*       1.创建流子类的对象,绑定数据源
*       2.调用对象的方法write()写入
*       3.close释放资源
*    流对象的构造方法可以创建文件,如果文件存在直接覆盖
* */
public class TestDemo {
    public static void main(String[] args) throws IOException{
        FileOutputStream fos = new FileOutputStream("e:\\test\\a.txt");
        //写入1个字节
        fos.write(100);
        //关闭资源
        fos.close();

        //写入字节数组
        FileOutputStream fos1 = new FileOutputStream("e:\\test\\b.txt");
        byte[] arr = {97,98,99,100};
        fos1.write(arr);
        //写入字节数组的一部分
        fos1.write(arr,1,2);
        fos1.close();

        //写入字符串的简便方法
        FileOutputStream fos2 = new FileOutputStream("e:\\test\\c.txt");
        String s = "Hello World";
        fos2.write(s.getBytes());
        fos2.close();
    }
}

1.1.3 给文件中续写和换行

  我们直接new FileOutputStream(file)这样创建对象,写入数据,会覆盖原有的文件,那么我们想在原有的文件中续写内容怎么办呢?
  继续查阅FileOutputStream的API。发现在FileOutputStream的构造函数中,可以接受一个boolean类型的值,如果值true,就会在文件末位继续添加。
构造方法

给文件中续写数据和换行,代码演示:

package com.qtw.api;

import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws Exception {
        File file = new File("e:\\test\\a.txt");
        FileOutputStream fos = new FileOutputStream(file, true);
        String str = "\r\n"+"Message ok";
        fos.write(str.getBytes());
        fos.close();
    }
}

1.1.4 IO异常的处理

  在前面编写代码中都发生了IO的异常。我们在实际开发中,对异常时如何处理的,我们来演示一下。

package com.qtw.api;

import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) {
        File file = new File("e:\\test\\a.txt");
        //定义FileOutputStream的引用
        FileOutputStream fos = null;
        try {
            //创建FileOutputStream对象
            fos = new FileOutputStream(file);
            //写出数据
            fos.write("abcde".getBytes());
        } catch (IOException ex) {
            System.out.println(ex.toString());
            throw new RuntimeException("文件写入失败,重试");
        } finally {
            //一定要判断fos是否为null,只有不为null时,才可以关闭资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException("文件释放资源失败");
                }
            }
        }
    }
}

1.2 字节输入流InputStream

  通过前面的学习,我们可以把内存中的数据写出到文件中,那如何想把内存中的数据读到内存中,我们通过InputStream可以实现。InputStream此抽象类,是表示字节输入流的所有类的超类。,定义了字节输入流的基本共性功能方法。

int read():读取一个字节并返回,没有字节返回-1。
int read(byte[]): 读取一定量的字节数,并存储到字节数组中,返回读取到的字节数,没有字节返回-1。

1.2.1 FileInputStream类

  InputStream有很多子类,其中子类FileInputStream可用来读取文件内容。
  FileInputStream 从文件系统中的某个文件中获得输入字节。

构造方法

1.2.2 FileInputStream类读取数据read方法

在读取文件中的数据时,调用read方法,实现从文件中读取数据

从文件中读取数据,代码演示:

package com.qtw.api;

import java.io.FileInputStream;
import java.io.File;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException{
        File file = new File("e:\\test\\a.txt");
        //创建一个字节输入流对象,必须明确数据源,其实就是创建字节读取流和数据源相关联。
        FileInputStream fis = new FileInputStream(file);
        //读取数据。使用 read();一次读一个字节。
        int ch = 0;
        while((ch=fis.read())!=-1){
            System.out.println("ch="+(char)ch);
        }
        fis.close();
    }
}

1.2.3 FileInputStream类读取数据read(byte[])方法

  在读取文件中的数据时,调用read方法,每次只能读取一个,太麻烦了,于是我们可以定义数组作为临时的存储容器,这时可以调用重载的read方法,一次可以读取多个字符。

package com.qtw.api;

import java.io.FileInputStream;
import java.io.File;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException{
        /*
         * 演示第二个读取方法, read(byte[]);
         */
        File file = new File("e:\\test\\a.txt");
        // 创建一个字节输入流对象,必须明确数据源,其实就是创建字节读取流和数据源相关联。
        FileInputStream fis = new FileInputStream(file);
        //创建一个字节数组。
        byte[] buf = new byte[2];
        int len = 0;
        while((len=fis.read(buf))!=-1){
            System.out.println(new String(buf,0,len));
        }
        fis.close();
    }
}

1.3 字节流练习

  既然会了文件的读和写操作了,那么我们就要在这个基础上进行更为复杂的操作。使用读写操作完成文件的复制。

1.3.1 复制文件

  原理;读取一个已有的数据,并将这些读到的数据写入到另一个文件中。

循环复制单个字符TestDemo.java

package com.qtw.api;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException{
        //1,明确源和目的。
        File srcFile = new File("e:\\test\\timg.jpg");
        File destFile = new File("e:\\test\\a.jpg");

        //2,明确字节流 输入流和源相关联,输出流和目的关联。
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);

        //3, 使用输入流的读取方法读取字节,并将字节写入到目的中。
        int ch = 0;
        while((ch=fis.read())!=-1){
            fos.write(ch);
        }
        //4,关闭资源。
        fos.close();
        fis.close();
    }
}

  上述代码输入流和输出流之间是通过ch这个变量进行数据交换的。
  上述复制文件有个问题,每次都从源文件读取一个,然后在写到指定文件,接着再读取一个字符,然后再写一个,一直这样下去。效率极低。
复制任何文件都行,ex:QQ.exe,因为所有的程序本质都是字节

1.3.2 缓冲数组方式复制文件

  上述代码复制文件效率太低了,并且频繁的从文件读数据,和写数据,能不能一次多把文件中多个数据都读进内容中,然后在一次写出去,这样的速度一定会比前面代码速度快。

package com.qtw.api;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException{
        File srcFile = new File("e:\\test\\timg.jpg");
        File destFile = new File("e:\\test\\b.jpg");
        // 明确字节流 输入流和源相关联,输出流和目的关联。
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);
        //定义一个缓冲区。
        byte[] buf = new byte[1024];
        int len = 0;
        while ((len = fis.read(buf)) != -1) {
            fos.write(buf, 0, len);// 将数组中的指定长度的数据写入到输出流中。
        }
        // 关闭资源。
        fos.close();
        fis.close();
    }
}

2 字符流

  经过前面的学习,我们基本掌握的文件的读写操作,在操作过程中字节流可以操作所有数据,可是当我们操作的文件中有中文字符,并且需要对中文字符做出处理时怎么办呢?

2.1 字节流读取字符的问题

通过以下程序读取带有中文件的文件

package com.qtw.api;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        //给文件中写中文
        writeCNText();
        //读取文件中的中文
        readCNText();
    }
    //读取中文
    public static void readCNText() throws IOException {
        FileInputStream fis = new FileInputStream("e:\\test\\a.txt");
        int ch = 0;
        while((ch = fis.read())!=-1){
            System.out.println(ch);
        }
    }
    //写中文
    public static void writeCNText() throws IOException {
        FileOutputStream fos = new FileOutputStream("e:\\test\\a.txt");
        fos.write("a北京欢迎你".getBytes());
        fos.close();
    }
}

  上面程序在读取含有中文的文件时,我们并没有看到具体的中文,而是看到一些数字,这是什么原因呢?既然看不到中文,那么我们如何对其中的中文做处理呢?要解决这个问题,我们必须研究下字符的编码过程。

2.2 字符输入流Reader

  上述程序中我们读取拥有中文的文件时,使用的字节流在读取,那么我们读取到的都是一个一个字节。只要把这些字节去查阅对应的编码表,就能够得到与之对应的字符。API中是否给我们已经提供了读取相应字符的功能流对象,Reader,读取字符流的抽象超类。

read():读取单个字符并返回
read(char[]):将数据读取到数组中,并返回读取的个数。

2.3.1 FileReader类

  查阅FileInputStream的API,发现FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。打开FileReader的API介绍。用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的

构造方法

2.2.2 FileReader读取包含中文的文件

使用FileReader读取包含中文的文件

package com.qtw.api;

import java.io.FileReader;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        //读取文件中的中文
        readCNText();
    }
    //读取中文
    public static void readCNText() throws IOException {
        FileReader fr = new FileReader("e:\\test\\a.txt");
        int ch = 0;
        while((ch = fr.read())!=-1){
            //输出的字符对应的编码值
            System.out.println(ch);
            //输出字符本身
            System.out.println((char)ch);
        }
    }
}

2.3 字符输出流Writer

  既然有专门用于读取字符的流对象,那么肯定也有写的字符流对象,查阅API,发现有一个Writer类,Writer是写入字符流的抽象类。其中描述了相应的写的动作。

2.3.1 FileWriter类

  查阅FileOutputStream的API,发现FileOutputStream 用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑使用 FileWriter。打开FileWriter的API介绍。用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。
构造方法

2.3.2 FileWriter写入中文到文件中

写入字符到文件中,先进行流的刷新,再进行流的关闭。

package com.qtw.api;

import java.io.FileWriter;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        //演示FileWriter 用于操作文件的便捷类。
        FileWriter fw = new FileWriter("e:\\test\\a.txt");
        fw.write("你好谢谢再见");//这些文字都要先编码。都写入到了流的缓冲区中。
        fw.flush();
        fw.close();

    }
}

2.4 flush()和close()的区别?

  flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用。
  close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后在关闭流。流不可以使用。如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭。

2.5 字符流练习

2.5.1 复制文本文件

练习:复制文本文件。
思路:

  1. 既然是文本涉及编码表。需要用字符流。
  2. 操作的是文件。涉及硬盘。
  3. 有指定码表吗?没有,默认就行。
    操作的是文件,使用的 默认码表。使用哪个字符流对象。直接使用字符流操作文件的便捷类。FileReader FileWriter
public class CopyTextFileTest {
    public static void main(String[] args) throws IOException {
        copyTextFile();
    }
    public static void copyTextFile() throws IOException {
        //1,明确源和目的。
        FileReader fr = new FileReader("c:\\cn.txt");
        FileWriter fw = new FileWriter("c:\\copy.txt");
        //2,为了提高效率。自定义缓冲区数组。字符数组。
        char[] buf = new char[1024];
        int len = 0;
        while((len=fr.read(buf))!=-1){
            fw.write(buf,0,len);
        }
        /*2,循环读写操作。效率低。
        int ch = 0;
        while((ch=fr.read())!=-1){
            fw.write(ch);
        }
        */
        //3,关闭资源。
        fw.close();
        fr.close();
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,289评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,968评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,336评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,718评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,734评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,240评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,631评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,599评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,139评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,166评论 3 345
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,286评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,917评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,604评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,075评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,205评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,814评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,351评论 2 365