Java第六章第4节:输入输出流( I/O流) 文件 读写二进制文件

简介

👨‍💻个人主页@云边牧风
👨‍🎓小编介绍:欢迎来到云边牧风破烂的小星球🌝
📋专栏:Java基础知识
🔑本章内容:输入输出流
记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~

这一节的主要内容有:

写二进制文件

读二进制文件

抽象类OutputStream

派生类BufferedOutputStream

一、二进制文件的读写

 1.1二进制文件

原则上讲, 所有文件都是由字节组成

 如果文件中的内容应被解释为字符,则文件被称为文本文件;否则为二进制文件

注意 一些文字处理软件(如 Word 等)产生的文件 中,数据要被解释为字体、格式、图形和其他非字符信息,因此,这样的文件 是二进制文件 ,不能用 Reader 流正确读取

1.2写二进制文件

 常用来写文件的输出流类:

FileOutputStream

 流程图:

 先用File类打开本地文件,实例化输入输出流,然后调用流的读写方法读取或写入数据,最后关闭流。

FileOutputStream(File file, boolean append);

FileOutputStream(String name, boolean append);

注意:append参数为true时,数据从文件尾部写入;append参数为false时,数据覆盖原文件。

使用FileOutputStream流可以写入字节数据到目标文件,FileOutputStream提供了单字节写入和byte数组写入两种方式。建议使用byte数组写入,将待写入的数据存储到一个byte数组中,然后再写入文件。当写入的文件已经存在时,需要指明写入方式是覆盖还是追加。

DataOutputStream

package java.io;

public class DataOutputStream extends FilterOutputStream implements DataOutput {
    // “数据输出流”的字节数
    protected int written;

    // “数据输出流”对应的字节数组
    private byte[] bytearr = null;

    // 构造函数
    public DataOutputStream(OutputStream out) {
        super(out);
    }

    // 增加“输出值”
    private void incCount(int value) {
        int temp = written + value;
        if (temp < 0) {
            temp = Integer.MAX_VALUE;
        }
        written = temp;
    }

    // 将int类型的值写入到“数据输出流”中
    public synchronized void write(int b) throws IOException {
        out.write(b);
        incCount(1);
    }

    // 将字节数组b从off开始的len个字节,都写入到“数据输出流”中
    public synchronized void write(byte b[], int off, int len)
        throws IOException
    {
        out.write(b, off, len);
        incCount(len);
    }

    // 清空缓冲,即将缓冲中的数据都写入到输出流中
    public void flush() throws IOException {
        out.flush();
    }

    // 将boolean类型的值写入到“数据输出流”中
    public final void writeBoolean(boolean v) throws IOException {
        out.write(v ? 1 : 0);
        incCount(1);
    }

    // 将byte类型的值写入到“数据输出流”中
    public final void writeByte(int v) throws IOException {
        out.write(v);
        incCount(1);
    }

    // 将short类型的值写入到“数据输出流”中
    // 注意:short占2个字节
    public final void writeShort(int v) throws IOException {
        // 写入 short高8位 对应的字节
        out.write((v >>> 8) & 0xFF);
        // 写入 short低8位 对应的字节
        out.write((v >>> 0) & 0xFF);
        incCount(2);
    }

    // 将char类型的值写入到“数据输出流”中
    // 注意:char占2个字节
    public final void writeChar(int v) throws IOException {
        // 写入 char高8位 对应的字节
        out.write((v >>> 8) & 0xFF);
        // 写入 char低8位 对应的字节
        out.write((v >>> 0) & 0xFF);
        incCount(2);
    }

    // 将int类型的值写入到“数据输出流”中
    // 注意:int占4个字节
    public final void writeInt(int v) throws IOException {
        out.write((v >>> 24) & 0xFF);
        out.write((v >>> 16) & 0xFF);
        out.write((v >>>  8) & 0xFF);
        out.write((v >>>  0) & 0xFF);
        incCount(4);
    }

    private byte writeBuffer[] = new byte[8];

    // 将long类型的值写入到“数据输出流”中
    // 注意:long占8个字节
    public final void writeLong(long v) throws IOException {
        writeBuffer[0] = (byte)(v >>> 56);
        writeBuffer[1] = (byte)(v >>> 48);
        writeBuffer[2] = (byte)(v >>> 40);
        writeBuffer[3] = (byte)(v >>> 32);
        writeBuffer[4] = (byte)(v >>> 24);
        writeBuffer[5] = (byte)(v >>> 16);
        writeBuffer[6] = (byte)(v >>>  8);
        writeBuffer[7] = (byte)(v >>>  0);
        out.write(writeBuffer, 0, 8);
        incCount(8);
    }

    // 将float类型的值写入到“数据输出流”中
    public final void writeFloat(float v) throws IOException {
        writeInt(Float.floatToIntBits(v));
    }

    // 将double类型的值写入到“数据输出流”中
    public final void writeDouble(double v) throws IOException {
        writeLong(Double.doubleToLongBits(v));
    }

    // 将String类型的值写入到“数据输出流”中
    // 实际写入时,是将String对应的每个字符转换成byte数据后写入输出流中。
    public final void writeBytes(String s) throws IOException {
        int len = s.length();
        for (int i = 0 ; i < len ; i++) {
            out.write((byte)s.charAt(i));
        }
        incCount(len);
    }

    // 将String类型的值写入到“数据输出流”中
    // 实际写入时,是将String对应的每个字符转换成char数据后写入输出流中。
    public final void writeChars(String s) throws IOException {
        int len = s.length();
        for (int i = 0 ; i < len ; i++) {
            int v = s.charAt(i);
            out.write((v >>> 8) & 0xFF);
            out.write((v >>> 0) & 0xFF);
        }
        incCount(len * 2);
    }

    // 将UTF-8类型的值写入到“数据输出流”中
    public final void writeUTF(String str) throws IOException {
        writeUTF(str, this);
    }

    // 将String数据以UTF-8类型的形式写入到“输出流out”中
    static int writeUTF(String str, DataOutput out) throws IOException {
        //获取String的长度
        int strlen = str.length();
        int utflen = 0;
        int c, count = 0;

        // 由于UTF-8是1~4个字节不等;
        // 这里,根据UTF-8首字节的范围,判断UTF-8是几个字节的。
        for (int i = 0; i < strlen; i++) {
            c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                utflen++;
            } else if (c > 0x07FF) {
                utflen += 3;
            } else {
                utflen += 2;
            }
        }

        if (utflen > 65535)
            throw new UTFDataFormatException(
                "encoded string too long: " + utflen + " bytes");

        // 新建“字节数组bytearr”
        byte[] bytearr = null;
        if (out instanceof DataOutputStream) {
            DataOutputStream dos = (DataOutputStream)out;
            if(dos.bytearr == null || (dos.bytearr.length < (utflen+2)))
                dos.bytearr = new byte[(utflen*2) + 2];
            bytearr = dos.bytearr;
        } else {
            bytearr = new byte[utflen+2];
        }

        // “字节数组”的前2个字节保存的是“UTF-8数据的长度”
        bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
        bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);

        // 对UTF-8中的单字节数据进行预处理
        int i=0;
        for (i=0; i<strlen; i++) {
           c = str.charAt(i);
           if (!((c >= 0x0001) && (c <= 0x007F))) break;
           bytearr[count++] = (byte) c;
        }

        // 对预处理后的数据,接着进行处理
        for (;i < strlen; i++){
            c = str.charAt(i);
            // UTF-8数据是1个字节的情况
            if ((c >= 0x0001) && (c <= 0x007F)) {
                bytearr[count++] = (byte) c;

            } else if (c > 0x07FF) {
                // UTF-8数据是3个字节的情况
                bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
                bytearr[count++] = (byte) (0x80 | ((c >>  6) & 0x3F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            } else {
                // UTF-8数据是2个字节的情况
                bytearr[count++] = (byte) (0xC0 | ((c >>  6) & 0x1F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            }
        }
        // 将字节数组写入到“数据输出流”中
        out.write(bytearr, 0, utflen+2);
        return utflen + 2;
    }

    public final int size() {
        return written;
    }
}

BufferedOutputStream

package java.io;

public class BufferedOutputStream extends FilterOutputStream {
    // 保存“缓冲输出流”数据的字节数组
    protected byte buf[];

    // 缓冲中数据的大小
    protected int count;

    // 构造函数:新建字节数组大小为8192的“缓冲输出流”
    public BufferedOutputStream(OutputStream out) {
        this(out, 8192);
    }

    // 构造函数:新建字节数组大小为size的“缓冲输出流”
    public BufferedOutputStream(OutputStream out, int size) {
        super(out);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

    // 将缓冲数据都写入到输出流中
    private void flushBuffer() throws IOException {
        if (count > 0) {
            out.write(buf, 0, count);
            count = 0;
        }
    }

    // 将“数据b(转换成字节类型)”写入到输出流中
    public synchronized void write(int b) throws IOException {
        // 若缓冲已满,则先将缓冲数据写入到输出流中。
        if (count >= buf.length) {
            flushBuffer();
        }
        // 将“数据b”写入到缓冲中
        buf[count++] = (byte)b;
    }

    public synchronized void write(byte b[], int off, int len) throws IOException {
        // 若“写入长度”大于“缓冲区大小”,则先将缓冲中的数据写入到输出流,然后直接将数组b写入到输出流中
        if (len >= buf.length) {
            flushBuffer();
            out.write(b, off, len);
            return;
        }
        // 若“剩余的缓冲空间 不足以 存储即将写入的数据”,则先将缓冲中的数据写入到输出流中
        if (len > buf.length - count) {
            flushBuffer();
        }
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }

    // 将“缓冲数据”写入到输出流中
    public synchronized void flush() throws IOException {
        flushBuffer();
        out.flush();
    }
}

 常用写字节的方法:

void write( int n); //写单个字节

void write( byte b[]);

void write( byte b[], int offset, int len);

1.3读二进制文件

 常用来读文件的输入流类:

FileInputStream

DataInputSteam

BufferedInputStream

 常用读二进制文件的方法:

int read();  //返回读取的单个字节的编码值

int read( byte b[]); //读取b.length个字节到字节数组b

int read( byte b[], int offset, int len);

二、抽象类OutputStream

 派生类FileOutputStream

用于一般目的输出(非字符输出)
用于成组字节输出

 派生类BufferedOutputStream

当创建 BufferedOutputStream 时,将 创建一个内部缓冲区数组。
BufferedOutputStream 存在缓冲读取功能。创建 BufferedOutputStream 时,我们会通过它的 构造函数指定某个输出流为参数

三、/写二进制文件实例

实例1:复制功能

import java.io.*;
import java.io.IOException;
public class Main {
    public static void main(String[] args) throws IOException {
        String fileName1 = "D:\\1234567890\\grade.txt";//定义文件的路径
        String fileName2 = "D:\\1234567890\\grade_2.txt";//定义文件的路径
        BufferedInputStream in = new BufferedInputStream(
                new   FileInputStream(fileName1));
        BufferedOutputStream out = new BufferedOutputStream(
                new FileOutputStream( fileName2  ) ) ;
        int c;
        byte buffer[]=new byte[1024];
        while((c=in.read(buffer))!=-1){
            for(int i=0;i<c;i++)
                out.write(buffer[i]);
        }
        in.close();
        out.close();
        
    }
}

实例2:剪切mp3

 int start = 2375680;//320kbps(比特率)*58s*1024/8=2375680 比特率可以查看音频属性获知

  int end = 4915200;//320kbps*120s*1024/8=4915200

四、派生类DataOutputStream

具有写各种基本数据类型的方法
将数据写到另一个输出流
它在所有的计算机平台上使用同样的数据格式
其常用的一些方法见表 6-2
其中 size 方法,可作为计数器,统计写入的字节数

4.1、写二进制文件

如:将三个int型数字255/0/-1写入数据文件data1.dat

import java.io.*;
import java.io.IOException;
public class Main {
    public static void main(String[] args) throws IOException {
        //String fileName1 = "D:\\1234567890\\grade.txt";//定义文件的路径
        //String fileName2 = "D:\\1234567890\\grade_2.txt";//定义文件的路径
        String fileName3 = "D:\\1234567890\\data1.dat" ;
        int value0  = 255, value1  = 0, value2 = -1;
        DataOutputStream out = new DataOutputStream(new FileOutputStream(fileName3));
        out.writeInt( value0 );
        out.writeInt( value1 );
        out.writeInt( value2 );
        out.close();


    }
}

运行结果

运行程序后,在对应文件夹内 生成数据文件 data1.dat
用写字板打开没有任何显示
ultraEdit 打开查看其二进制信息 内容为 00 00 00 FF 00 00 00 00 FF FF FF FF 每个 int 数字都是32个 bit

 说明

FileOutputStream 类的构造方法负责打开文件“ data1.dat” 用于写数据
FileOutputStream 类的对象与 DataOutputStream 对象连接,写基本类型的数据

五、派生类BufferedOutputStream

写二进制文件的缓冲流类
类似于文本文件中的 BufferedWriter
对于大量数据的写入,可提高效率
用法示例:

DataOutputStream out = new DataOutputStream(new BufferedOutputStream(

new FileOutputStream( fileName ) ) );

如:向文件中写入各种数据类型的数,并统计写入的字节数

import java.io.*;
import java.io.IOException;
public class Main {
    public static void main(String[] args) throws IOException {
        //String fileName1 = "D:\\1234567890\\grade.txt";//定义文件的路径
        String fileName = "D:\\1234567890\\mixedTypes.dat" ;
        DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(
                new FileOutputStream(fileName)));
        dataOut.writeInt( 0 );
        System.out.println( dataOut.size()  + " bytes have been written.");
        dataOut.writeDouble( 31.2 );
        System.out.println( dataOut.size()  + " bytes have been written.");
        dataOut.writeBytes("JAVA");
        System.out.println( dataOut.size()  + " bytes have been written.");
        dataOut.close();


    }
}

输出为: 

4 bytes have been written.
12 bytes have been written.
16 bytes have been written.

结束语:

以上是Jav第六章第4节的全部内容 希望大家喜欢

下一节讲第六章第5节:文件读写—— 随机存取文件读写

喜欢的可以点赞+关注哈 ❤

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云边牧风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值