java基础类库——利用字节流和字符流实现文件内容操作(十二)

利用字节流和字符流实现文件内容操作

如果要进行文件内容的操作,那么必须依靠数据流完成,而数据流主要分为两种
(1)字节流:InputStream(字节输入流)、OutputStream(字节输出流)
在这里插入图片描述

(2)字符流:Reader(字符输入流)、Writer(字符输出流)
在这里插入图片描述

字符要比字节处理的更多一些。但是不管使用的是字节流还是字符流,其基本的操作流程都是一样的,以文件操作为例:
1.创建File类对象,主要是指明要操作的文件路径;
2.通过字节流或字符流的子类为父类实例化;
3.进行文件的读、写操作;
4.关闭数据流(close())。

1.1 字节输出流:OutputStream

字节输出流主要是以操作byte数据为主的,首先来观察java.io.OutputStream的定义结构:public abstract class OutputStream implements Closeable, Flushable {}
OutputStream继承了Closeable和Flushable接口

在这里插入图片描述

除了close()和flush()两个方法之外,还定义有三个重要的输出操作方法。
(1)输出单个字节:public abstract void write(int b) throws IOException;
(2)输出全部字节数组:public void write(byte b[]) throws IOException;
(3)输出部分数组:public void write(byte b[], int off, int len) throws IOException;

但是OutputStream是一个抽象类,按照抽象类的基本原则来讲,要想去等OutputStream类的实例化对象,那么一定要依靠子类,如果要进行文件的输出操作则可以使用FileOutputStream,在这个类中提供了两个常用构造
构造方法:public FileOutputStream(File file) throws FileNotFoundException
构造方法: public FileOutputStream(File file, boolean append) throws FileNotFoundException

范例:实现文件的输出

package day2;


import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class FileDemo {
    public static void main(String[] args) throws Exception{
        //第一步:定义要输出的File对象
        File file =  new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
        //输出信息的时候文件可以不存在,但是目录必须存在
        if(!file.getParentFile().exists()){//父路径不存在
            file.getParentFile().mkdirs();//创建父路径
        }
        //第二部:利用OutputStream的子类为父类进行实例化
        OutputStream outputStream = new FileOutputStream(file);
        //第三部:输出文字信息
        String msg = "小猫从小有个梦,想去嵩山少林学武功!!!";
        //为了方便输出,需要把字符串变为字节数组
        byte data[] = msg.getBytes();
        //输出数据
        outputStream.write(data);
        outputStream.close();
    }
}

范例:输出部分数据

package day2;


import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class FileDemo {
    public static void main(String[] args) throws Exception{
        //第一步:定义要输出的File对象
        File file =  new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
        //输出信息的时候文件可以不存在,但是目录必须存在
        if(!file.getParentFile().exists()){//父路径不存在
            file.getParentFile().mkdirs();//创建父路径
        }
        //第二部:利用OutputStream的子类为父类进行实例化
        OutputStream outputStream = new FileOutputStream(file);
        //第三部:输出文字信息
        String msg = "小猫从小有个梦,想去嵩山少林学武功!!!";
        //为了方便输出,需要把字符串变为字节数组
        byte data[] = msg.getBytes();
        //输出数据
        outputStream.write(data,2,14);
        outputStream.close();
    }
}
循环输出:
for(int i=0;i<data.length;i++){
	outputStream.write(data[i]);
}

但是发现每当执行完成之后所有的内容都被覆盖了,所以也可以进行数据的追加操作。

范例:追加数据
//第二部:利用OutputStream的子类为父类进行实例化
OutputStream outputStream = new FileOutputStream(file,true);

如果换行使用:\r\n
//第三部:输出文字信息
String msg = “小猫从小有个梦,想去嵩山少林学武功!!!\r\n”;

1.2 字节输入流:InputStream
InputStream可以实现数据读取操作,在java中的InputStream类中的定义如下:
在这里插入图片描述

在InputStream类中定义有三个读取数据的读取数据操作方法:
(1)读取单个字节:public abstract int read() throws IOException;
每次执行此方法读取单个字节数据,如果已经读取完成了,那么最后返回的是-1;
在这里插入图片描述

(2)读取数据到字节数组中:public int read(byte b[]) throws IOException ;
在这里插入图片描述

每次将数据读取到数组之中,那么会返回一个读取长度的数据,如果没有数据 返回的长度为-1,可是要考虑两种情况:1.要读取的内容大于开辟的数组内容,长度就是整个数组的长度;2.要读取的内容小于开辟的数组内容,长度就是全部最后的内容长度,数组装不满;

(3)读取部分数据到字节数组中:public int read(byte b[], int off, int len) throws IOException;
每次读取的内容到部分字节数组,只允许读取满限制的数组的字节个数,此方法依然会返回读取的长度;
整个操作跟输出的形式几乎是相同的,参数 的类型及作用也几乎是相同的

InputStream是一个抽象类,所以要进行文件的读取使用FileInputStream子类,子类定义的构造方法如下:构造方法:public FileInputStream(File file) throws FileNotFoundException

范例:实现数据读取

package day2;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class FileDemo {
    public static void main(String[] args) throws Exception{
        //第一步:定义要输出的File对象
        File file =  new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
        //第二部:实例化InputStream对象
        InputStream inputStream = new FileInputStream(file);
        //实现数据的读取操作
        byte data[] = new byte[1024];
        int len = inputStream.read(data);//将数据读取到数组之中
        System.out.println("读取的内容:【"+new String(data,0,len) + "】");
        inputStream.close();
    }
}

在InputStream类中提供了read()方法,这个方法可以实现单个字节数据读取操作,下面运用循环的方法采用此方法 实现单个字节数据读取

范例:读取单个字节

package day2;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class FileDemo {
    public static void main(String[] args) throws Exception{
        //第一步:定义要输出的File对象
        File file =  new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
        //第二部:实例化InputStream对象
        InputStream inputStream = new FileInputStream(file);
        //实现数据的读取操作
        byte data[] = new byte[1024];
        int foot = 0;
        int temp = 0;
        do{
            temp = inputStream.read();
            if(temp != -1){
                data[foot ++]  = (byte) temp;
            }
        }while (temp != -1);
        System.out.println("读取的内容:【"+new String(data,0,foot) + "】");
        inputStream.close();
    }
}

以上使用的是do…while循环操作,但是太麻烦,在实际中一般会采用while实现循环操作

package day2;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class FileDemo {
    public static void main(String[] args) throws Exception{
        //第一步:定义要输出的File对象
        File file =  new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
        //第二部:实例化InputStream对象
        InputStream inputStream = new FileInputStream(file);
        //实现数据的读取操作
        byte data[] = new byte[1024];
        int foot = 0;
        int temp = 0;
        while ((temp = inputStream.read()) != -1){
            data[foot++] = (byte) inputStream.read();
        }
        System.out.println("读取的内容:【"+new String(data,0,foot) + "】");
        inputStream.close();
    }
}

1.3 字符输出流Writer
定义: JDK1.1
public abstract class Writer implements Appendable, Closeable, Flushable {
}
Writer是进行字符输出操作的使用的类,这个类是属于抽象类
范例:

在这里插入代码片
package day2;
import java.io.*;

public class FileDemo {
    public static void main(String[] args) throws Exception{
        //第一步:定义要输出的File对象
        File file =  new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        Writer out = new FileWriter(file);
        String date = "哈哈哈,好好生活,天天向上";
        out.write(date);
        out.close();
    }
}

虽然Writer类提供有字符数组的输出能力,但是从本质上来讲使用Writer类就意味着要执行字符串的直接输出。字符流是最适合操作中文的,但是并不是意味着字节流就无法操作中文。

1.4字符输入流Reader
Reader也是一个抽象类,
范例:

package day2;
import java.io.*;

public class FileDemo {
    public static void main(String[] args) throws Exception{
        //第一步:定义要输出的File对象
        File file =  new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
        if(file.getParentFile().exists()){
           Reader reader = new FileReader(file);
           char data[] = new char[1024];
           //向字符数组保存数据,返回长度
           int len = reader.read(data);
            System.out.println(new String(data,0,len));
            reader.close();
        }

    }

Reader与InputStream类相比出来数据类型的差别之外,操作上没有优势。

1.5 字节流与字符流的区别
这两种流的区别就好比数据库中的BLOB,CLOB
CLOB:保存大文本数据;——>字符数据
BLOB:保存二进制数据,例如:电影、图片、文字 ——>字节数据

通过任何终端(网络、文件)读取或者输出的数据都一定是字节,但字符是经常处理后的数据。
字符输入:字节(磁盘)自动转换为字符(内存)
字符输出:字符(内存)自动转换为字节(磁盘)

在利用字符流输出的时候,所有的内容实际上都只是输出到缓冲区中(内存)。在使用close()方法关闭的时候会将缓冲区的数据输出,如果没有关别,那么就将无法进行输出,此时可以利用flush()进行强制刷新。

字符使用到了缓冲区,而字节没有使用到缓冲区。
注:如果处理中文使用字符流,其他的任何数据都使用字节流,字节流和字符流是可以相互转换的。

1.6 转换流
在现在给出的字节流与字符流的操作之中,可以发现是各有各的特点,为此在java中提供了两个转换的流:InputStreamReader、OutputStreamWriter。
InputStreamReader
public class InputStreamReader extends Reader {}
public InputStreamReader(InputStream in
在这里插入图片描述

OutputStreamWriter
public class OutputStreamWriter extends Writer {}
public OutputStreamWriter(OutputStream out)
图26

范例

package day2;
import java.io.*;

public class FileDemo {
    public static void main(String[] args) throws Exception{
        //第一步:定义要输出的File对象
        File file =  new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
        if(file.getParentFile().exists()){
        OutputStream outputStream = new FileOutputStream(file);
        //因为writer存在有直接输出字符串的方法
        Writer out = new OutputStreamWriter(outputStream);
        out.write("情深深雨蒙蒙!!!");
        out.close();
        outputStream.close();
        }
    }
}

1.7 文件复制(综合运用)
文件复制是模拟dos系统中的copy命令完成。
编写一个文件拷贝程序,可以实现任意的文件拷贝操作,通过初始化输入参数拷贝的源文件路径以及拷贝的目标文件路径,本程序可以暂拷贝命令:copy e:\my.jpg e:\hello.jpg
如果要想实现这种拷贝操作,可以有以下两种操作思路:
(1)开辟一个数组,将所需拷贝的内容读取到数组之中,而后一次性输出到目标文件路径中;
(2)采用边读边写的方式进行拷贝,不是一次性读取
注:第一种方法问题在于,如果文件小没有问题,5M左右,如果文件大,基本上内存就被占满了

范例:初期实现

package day2;

import java.io.*;

public class CopyDemo {
    public static void main(String[] args) throws Exception {
        //如果只选拷贝命令,必须通过初始化参数传递源文件路径及目标文件路径
        if(args.length != 2){//参数文件必须是两个
            System.out.println("错误的命令,格式应该为 copy 源文件路径 目标文件路径");
            //退出程序操作
            System.exit(1);

            //验证源文件是否存在
            File inFile = new File(args[0]);
            if(!inFile.exists()){
                System.out.println("源文件路径不存在");
                System.exit(1);
            }

            //验证目标文件是否已经存在
            File outFile = new File(args[1]);
            if(!outFile.exists()){
                System.out.println("目标文件已存在");
                System.exit(1);
            }
            long startTime = System.currentTimeMillis();
            InputStream input = new FileInputStream(inFile);
            OutputStream output = new FileOutputStream(outFile);
            copy(input,output);
            input.close();
            output.close();
            long endTime = System.currentTimeMillis();
            System.out.println("拷贝文件花费时间:" + (endTime - startTime));
        }
    }
    public static void copy(InputStream input,OutputStream output) throws Exception{
        //保存每次读取的字节量
        int temp = 0;
        while ((temp = input.read()) != -1){//每次读取一个字节
            output.write(temp);
        }
    }
}

以上代码执行太慢了,可以利用数组来提升性能。将数据读取到数组中,而后一次性将数组输出。
在这里插入图片描述

范例:

package day2;

import java.io.*;

public class CopyDemo {
    public static void main(String[] args) throws Exception {
        //如果只选拷贝命令,必须通过初始化参数传递源文件路径及目标文件路径
        if(args.length != 2){//参数文件必须是两个
            System.out.println("错误的命令,格式应该为 copy 源文件路径 目标文件路径");
            //退出程序操作
            System.exit(1);

            //验证源文件是否存在
            File inFile = new File(args[0]);
            if(!inFile.exists()){
                System.out.println("源文件路径不存在");
                System.exit(1);
            }

            //验证目标文件是否已经存在
            File outFile = new File(args[1]);
            if(!outFile.exists()){
                System.out.println("目标文件已存在");
                System.exit(1);
            }
            long startTime = System.currentTimeMillis();
            InputStream input = new FileInputStream(inFile);
            OutputStream output = new FileOutputStream(outFile);
            copy(input,output);
            input.close();
            output.close();
            long endTime = System.currentTimeMillis();
            System.out.println("拷贝文件花费时间:" + (endTime - startTime));
        }
    }
    public static void copy(InputStream input,OutputStream output) throws Exception{
        //保存每次读取的数量
        int temp = 0;
        byte data[] = new byte[1024];
        //数据向数组中读取
        while ((temp = input.read(data)) != -1){
            output.write(data,0,temp);
        }
    }
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值