二进制字节流_file 转为二进制字节流 - CSDN
精华内容
参与话题
  • js发送和接收二进制字节流数据

    万次阅读 2019-03-15 16:28:44
    发送二进制数据 var oReq = new XMLHttpRequest(); oReq.open("POST", url, true); oReq.onload = function (oEvent) { // Uploaded. }; var blob = new Blob(['abc123'], {type: 'text/plain'})...
    • 发送二进制数据
    var oReq = new XMLHttpRequest();
    oReq.open("POST", url, true);
    oReq.onload = function (oEvent) {
    // Uploaded.
    };
    var blob = new Blob(['abc123'], {type: 'text/plain'});
    oReq.send(blob);
    

    或者

    var myArray = new ArrayBuffer(512);
    var longInt8View = new Uint8Array(myArray);
    for (var i=0; i< longInt8View.length; i++) {
    longInt8View[i] = i % 255;
    }
    var xhr = new XMLHttpRequest;
    xhr.open("POST", url, false);
    xhr.send(myArray);
    
    • 接收二进制数据
    var oReq = new XMLHttpRequest();
    oReq.open("GET", "/myfile.png", true);
    oReq.responseType = "arraybuffer";
    oReq.onload = function (oEvent) {
    var arrayBuffer = oReq.response; // Note: not oReq.responseText
    if (arrayBuffer) {
    var byteArray = new Uint8Array(arrayBuffer);
    for (var i = 0; i < byteArray.byteLength; i++) {
    }
    }
    };
    oReq.send(null);
    

    如上设置只能是文本类型,如果是Blob类型:

    var oReq = new XMLHttpRequest();
    oReq.open("GET", "/myfile.png", true);
    oReq.responseType = "arraybuffer";
    oReq.onload = function(oEvent) {
    var blob = new Blob([oReq.response], {type: "image/png"});
    // ...
    };
    oReq.send();
    

    或者

    var oReq = new XMLHttpRequest();
    oReq.open("GET", "/myfile.png", true);
    oReq.responseType = "blob";
    oReq.onload = function(oEvent) {
    var blob = oReq.response;
    // ...
    };
    oReq.send();
    

    旧版本的浏览器:

    function load_binary_resource(url) {
    var req = new XMLHttpRequest();
    req.open('GET', url, false);
    //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
    req.overrideMimeType('text\/plain; charset=x-user-defined');
    req.send(null);
    if (req.status != 200) return '';
    return req.responseText;
    }
    

    以上参考https://www.jb51.net/article/85087.htm

    ArrayBuffer和TypedArray,以及Blob的使用
    var ab = new ArrayBuffer(32)
    var iA = new Int8Array(ab)
    iA[0] = 97;//把二进制的数据的首位改为97 ,97为小写字母a的ascll码;
    var blob = new Blob([iA], {type: "application/octet-binary"});//把二进制的码转化为blob类型
    var url = URL.createObjectURL(blob);
    window.open(url)
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <input type="file" id="f">
        <script>
            var eF = document.getElementById("f");
            eF.onchange = function() {
                var file = eF.files[0];
                console.log(file instanceof Blob)
            }
        </script>
    </body>
    </html>
    

    FileReader读区blob文件

    var ab = new ArrayBuffer(32)
    var iA = new Int8Array(ab)
    iA[0] = 97
    var blob = new Blob([iA], {type: "application/octet-binary"});
    var fr = new FileReader();
    fr.addEventListener("load", function(ev) {
        console.log(ev.target.result);//会输出字符:a
    });
    fr.readAsText(blob)
    

    blob转化为typedArray
    如果要把blob文件转化为二进制的数据的话,要先把blob转化为arraybuffer,然后再使用typedArray就可以直接编辑二进制数据

    var ab = new ArrayBuffer(32)
    var iA = new Int8Array(ab)
    iA[0] = 97
    var blob = new Blob([iA], {type: "application/octet-binary"});
    var fr = new FileReader();
    fr.addEventListener("load", function(ev) {
        var abb = ev.target.result;
        var iAA = new Int8Array(abb);
        console.log(iAA);
    });
    //把blob文件转化为arraybuffer;
    fr.readAsArrayBuffer(blob)
    

    以上参考https://www.cnblogs.com/diligenceday/p/5998806.html

    推荐文章 :HTML5新特性之文件和二进制数据的操作

    展开全文
  • Java字节、二进制字节流、字符

    千次阅读 2019-06-24 06:15:59
    Java字节、二进制字节流、字符

    1 计算机基础

    1.1 二进制

    二进制数据是用0和1两个数码来表示的数:它的基数为2,进位规则是"逢二进一",借位规则是"借一当二"。
    当前的计算机系统使用的基本上是二进制系统,数据在计算机中主要是以补码的形式存储的。

    1.1.1 bit Byte KB MB关系

    • 1 bit
      计算机中的二进制位(bit)是长度单位,是一个非常微小的开关,用"开"来表示1,"关"来表示0。
      因为计算机通信和存储的时候都是以010101这样的二进制数据为基础的,这儿的一个0和1占的地方就叫bit(位),即一个二进制位。
    • 2 Byte
      字(Byte)节也是长度单位。
      Byte是一个字节保存的,有8个bit位,即8个0或1,8位的第一个位是符号位。
    00000001[二进制]=1[十进制] 
    10000000[二进制]=-1[十进制]
    二进制区间[127~-128]:
    正: 01111111[二进制]=127 [十进制] 
    正: 11111111[二进制]=-128 [十进制] 
    负数最大为1111 1111,也就是数字-128
    
    • 3 单位关系
    单位 关系
    1Byte 8bit
    1KB 1024B
    1MB 1024KB
    1GB 1024MB

    1.2 八进制

    Octal,缩写OCT或O,一种以8为基数的计数法,采用0,1,2,3,4,5,6,7八个数字,逢八进1。
    一些编程语言中常常以数字0开始表明该数字是八进制,八进制的数和二进制数可以按位对应(八进制一位对应二进制三位),因此常应用在计算机语言中。

    1.3 十六进制

    十六进制(英文名称:Hexadecimal),是计算机中数据的一种表示方法。同我们日常生活中的表示法不一样。
    它由0-9,A-F组成,字母不区分大小写,与10进制的对应关系是:0-9对应0-9;A-F对应10-15;N进制的数可以用0~(N-1)的数表示,超过9的用字母A-F。

    2 Java中的各进制、字节流和字符流

    2.1 代码中使用

    • 1 代码表示
      0b开始的数据表示二进制
      0开始的数据表示八进制
      0x开始的数据表示十六进制
    • 2 打印
      打印二进制Integer.toBinaryString(int i)
      打印八进制Integer.toOctalString(int i)
      打印十六进制Integer.toHexString(int i)

    2.2 Java的基本数据类型长度

    数据类型 占用字节
    char 2个Byte(2*8bit)
    short 2个Byte(2*8bit)
    int 4个Byte(4*8bit)
    long 8个Byte(8*8bit)
    float 4个Byte(4*8bit)
    double 8个Byte(8*8bit)

    2.3 字节流和字符流

    程序中所有的数据都是以流的方式进行传输或保存的,流中保存的实际上是字节文件,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成。

    2.3.1 字节流与字符流

    java.io包中操作文件内容的主要有两大类:字节流字符流,两类都分为输入输出操作。

    在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成(这四个都是抽象类)。
    在这里插入图片描述

    2.3.2 Java.io包

    Java.io包其中包括:InputStream,OutputStream,Reader,Writer;

    • InputStreamOutputStream:为字节流,主要用来处理字节或二进制对象。
      如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点,所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。
      在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列,字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串;

    • ReaderWriter:为字符流(一个字符占两个字节),主要用来处理字符或字符串。
      字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。
      所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好。

    字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的。

    实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联 在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的 。

    2.3.3 字节流与字符流的区别

    • 1 字节流和字符流使用是非常相似的,那么除了操作代码的不同之外,还有哪些不同呢?

    字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的。
    字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容

    • 2 那开发中究竟用字节流好还是用字符流好呢?

    在所有的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,而字符是只有在内存中才会形成的,所以使用字节的操作是最多的。
    如果要java程序实现一个拷贝功能,应该选用字节流进行操作(可能拷贝的是图片),并且采用边读边写的方式(节省内存)。

    3 原码、反码、补码

    我们来谈谈另一个话题:原码反码补码
    计算机中,所有数据最终都是使用二进制数表达。
    负数如何用二进制表达呢
    在计算机中,负数以其正值的补码形式表达。

    3.1 原码:一个整数,按照绝对值大小转换成的二进制数,称为原码。

    假设有一个int 类型的数,值为5,那么,我们知道它在计算机中表示为:5
    其原码如下:
    00000000|00000000|00000000|00000101

    3.2 反码:将二进制数按位取反,所得的新二进制数称为原二进制数的反码。

    取反操作指:原为1,得0;原为0,得1。(1变0; 0变1)
    其反码如下:
    11111111|11111111|11111111|11111010

    3.3 补码:反码加1称为补码。

    其补码如下:
    11111111|11111111|11111111|11111010 + 1 = 11111111|11111111|11111111|11111011

    再举一例:
    -1在计算机中如何表示。
    1、先取1的原码:
    00000000|00000000|00000000|00000001
    2、得反码:
    11111111|11111111|11111111|11111110
    3、得补码:
    11111111|11111111|11111111|11111111
    可见-1在计算机里用二进制表达就是全1,16进制为:0xFFFFFFFF。

    展开全文
  • 1.字节: 字(Byte)节是长度单位。...因为计算机通信和存储的时候都是以010101这样的二进制数据为基础的,这儿的一个0和1占的地方就叫bit(位),即一个二进制位。 1Byte=8bit 1KB=1024B 1...

    https://blog.csdn.net/changwilling/article/details/52065955

    1.字节:

        字(Byte)节是长度单位。位(bit)也是长度单位。

    因为计算机通信和存储的时候都是以010101这样的二进制数据为基础的,这儿的一个0和1占的地方就叫bit(位),即一个二进制位。

    1Byte=8bit

    1KB=1024B

    1MB=1024KB(2的十次方)

    2.二进制

     二进制数有两个特点:它由两个基本字符0,1组成,二进制数运算规律是逢二进一。 

    为区别于其它进制数,二进制数的书写通常在数的右下方注上基数2,或加后面加B表示。 

    例如:二进制数10110011可以写成(10110011)2,或写成10110011B,对于十进制数可以不加注.计算机中的数据均采用二进制数表示,这是因为二进制数具有以下特点: 

    1) 二进制数中只有两个字符0和1,表示具有两个不同稳定状态的元器件。例如,电路中有,无电流,有电流用1表示,无电流用0表示。类似的还比如电路中电压的高,低, 晶体管的导通和截止等。 

    2) 二进制数运算简单,大大简化了计算中运算部件的结构。 

    二进制数的加法和乘法运算如下: 

    0+0=0 0+1=1+0=1 1+1=10 

    0×0=0 0×1=1×0=0 1×1=1 

    由于二进制数在使用中位数太长,不容易记忆,所以又提出了十六进制数. 

    3.字节流和字符流

    概念

           在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成。

    程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。

    字节流与字符流

    在java.io包中操作文件内容的主要有两大类:字节流、字符流,两类都分为输入和输出操作。在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。(这四个都是抽象类)

    java中提供了专用于输入输出功能的包Java.io,其中包括:

         InputStream,OutputStream,Reader,Writer

         InputStream 和OutputStream,两个是为字节流设计的,主要用来处理字节或二进制对象,

         Reader和 Writer.两个是为字符流(一个字符占两个字节)设计的,主要用来处理字符或字符串.

    字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点

         所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列

          字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串; 2. 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以

           字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的 但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联 在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的 

     

        区别

        字节流与字符流的区别

    字节流和字符流使用是非常相似的,那么除了操作代码的不同之外,还有哪些不同呢?

    字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的

    字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容

    那开发中究竟用字节流好还是用字符流好呢?

    在所有的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,而字符是只有在内存中才会形成的,所以使用字节的操作是最多的。

    如果要java程序实现一个拷贝功能,应该选用字节流进行操作(可能拷贝的是图片),并且采用边读边写的方式(节省内存)。

     

    转载于:https://www.cnblogs.com/renjiaqi/p/11421725.html

    展开全文
  • 二进制文件和字节流

    千次阅读 2019-02-24 16:40:34
    本节我们介绍在Java中如何以二进制字节的方式来处理文件,上节我们提到Java中有的概念,以二进制方式读写的主要有: InputStream/OutputStream: 这是基类,它们是抽象类。 FileInputStream/FileOutputStream: ...

    本节我们介绍在Java中如何以二进制字节的方式来处理文件,上节我们提到Java中有流的概念,以二进制方式读写的主要流有:

    • InputStream/OutputStream: 这是基类,它们是抽象类。
    • FileInputStream/FileOutputStream: 输入源和输出目标是文件的流。
    • ByteArrayInputStream/ByteArrayOutputStream: 输入源和输出目标是字节数组的流。
    • DataInputStream/DataOutputStream: 装饰类,按基本类型和字符串而非只是字节读写流。
    • BufferedInputStream/BufferedOutputStream: 装饰类,对输入输出流提供缓冲功能。

    下面,我们就来介绍这些类的功能、用法、原理和使用场景,最后,我们总结一些简单的实用方法。

    InputStream/OutputStream

    InputStream的基本方法

    InputStream是抽象类,主要方法是:

    public abstract int read() throws IOException;

    read从流中读取下一个字节,返回类型为int,但取值在0到255之间,当读到流结尾的时候,返回值为-1,如果流中没有数据,read方法会阻塞直到数据到来、流关闭、或异常出现,异常出现时,read方法抛出异常,类型为IOException,这是一个受检异常,调用者必须进行处理。read是一个抽象方法,具体子类必须实现,FileInputStream会调用本地方法,所谓本地方法,一般不是用Java写的,大多使用C语言实现,具体实现往往与虚拟机和操作系统有关。

    InputStream还有如下方法,可以一次读取多个字节:

    public int read(byte b[]) throws IOException

    读入的字节放入参数数组b中,第一个字节存入b[0],第二个存入b[1],以此类推,一次最多读入的字节个数为数组b的长度,但实际读入的个数可能小于数组长度,返回值为实际读入的字节个数。如果刚开始读取时已到流结尾,则返回-1,否则,只要数组长度大于0,该方法都会尽力至少读取一个字节,如果流中一个字节都没有,它会阻塞,异常出现时也是抛出IOException。该方法不是抽象方法,InputStream有一个默认实现,主要就是循环调用读一个字节的read方法,但子类如FileInputStream往往会提供更为高效的实现。

    批量读取还有一个更为通用的重载方法:

    public int read(byte b[], int off, int len) throws IOException

    读入的第一个字节放入b[off],最多读取len个字节,read(byte b[])就是调用了该方法:

    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    流读取结束后,应该关闭,以释放相关资源,关闭方法为:

    public void close() throws IOException

    不管read方法是否抛出了异常,都应该调用close方法,所以close通常应该放在finally语句内。close自己可能也会抛出IOException,但通常可以捕获并忽略。

    InputStream的高级方法

    InputStream还定义了如下方法:

    public long skip(long n) throws IOException
    public int available() throws IOException
    public synchronized void mark(int readlimit)
    public boolean markSupported()
    public synchronized void reset() throws IOException

    skip跳过输入流中n个字节,因为输入流中剩余的字节个数可能不到n,所以返回值为实际略过的字节个数。InputStream的默认实现就是尽力读取n个字节并扔掉,子类往往会提供更为高效的实现,FileInputStream会调用本地方法。在处理数据时,对于不感兴趣的部分,skip往往比读取然后扔掉的效率要高。

    available返回下一次不需要阻塞就能读取到的大概字节个数。InputStream的默认实现是返回0,子类会根据具体情况返回适当的值,FileInputStream会调用本地方法。在文件读写中,这个方法一般没什么用,但在从网络读取数据时,可以根据该方法的返回值在网络有足够数据时才读,以避免阻塞。

    一般的流读取都是一次性的,且只能往前读,不能往后读,但有时可能希望能够先看一下后面的内容,根据情况,再重新读取。比如,处理一个未知的二进制文件,我们不确定它的类型,但可能可以通过流的前几十个字节判断出来,判读出来后,再重置到流开头,交给相应类型的代码进行处理。

    InputStream定义了三个方法,mark/reset/markSupported,用于支持从读过的流中重复读取。怎么重复读取呢?先使用mark方法将当前位置标记下来,在读取了一些字节,希望重新从标记位置读时,调用reset方法。

    能够重复读取不代表能够回到任意的标记位置,mark方法有一个参数readLimit,表示在设置了标记后,能够继续往后读的最多字节数,如果超过了,标记会无效。为什么会这样呢?因为之所以能够重读,是因为流能够将从标记位置开始的字节保存起来,而保存消耗的内存不能无限大,流只保证不会小于readLimit。

    不是所有流都支持mark/reset的,是否支持可以通过markSupported的返回值进行判断。InpuStream的默认实现是不支持,FileInputStream也不直接支持,但BufferedInputStream和ByteArrayInputStream可以。

    OutputStream

    OutputStream的基本方法是:

    public abstract void write(int b) throws IOException;

    向流中写入一个字节,参数类型虽然是int,但其实只会用到最低的8位。这个方法是抽象方法,具体子类必须实现,FileInputStream会调用本地方法。

    OutputStream还有两个批量写入的方法:

    public void write(byte b[]) throws IOException
    public void write(byte b[], int off, int len) throws IOException

    在第二个方法中,第一个写入的字节是b[off],写入个数为len,最后一个是b[off+len-1],第一个方法等同于调用:write(b, 0, b.length);。OutputStream的默认实现是循环调用单字节的write方法,子类往往有更为高效的实现,FileOutpuStream会调用对应的批量写本地方法。

    OutputStream还有两个方法:

    public void flush() throws IOException
    public void close() throws IOException

    flush将缓冲而未实际写的数据进行实际写入,比如,在BufferedOutputStream中,调用flush会将其缓冲区的内容写到其装饰的流中,并调用该流的flush方法。基类OutputStream没有缓冲,flush代码为空。

    需要说明的是文件输出流FileOutputStream,你可能会认为,调用flush会强制确保数据保存到硬盘上,但实际上不是这样,FileOutputStream没有缓冲,没有重写flush,调用flush没有任何效果,数据只是传递给了操作系统,但操作系统什么时候保存到硬盘上,这是不一定的。要确保数据保存到了硬盘上,可以调用FileOutputStream中的特有方法。

    close一般会首先调用flush,然后再释放流占用的系统资源。同InputStream一样,close一般应该放在finally语句内。

    FileInputStream/FileOutputStream

    FileOutputStream

    FileOutputStream的主要构造方法有:

    public FileOutputStream(File file) throws FileNotFoundException
    public FileOutputStream(File file, boolean append) throws FileNotFoundException
    public FileOutputStream(String name) throws FileNotFoundException
    public FileOutputStream(String name, boolean append) throws FileNotFoundException

    有两类参数,一类是文件路径,可以是File对象file,也可以是文件路径name,路径可以是绝对路径,也可以是相对路径,如果文件已存在,append参数指定是追加还是覆盖,true表示追加,没传append参数表示覆盖。new一个FileOutputStream对象会实际打开文件,操作系统会分配相关资源。如果当前用户没有写权限,会抛出异常SecurityException,它是一种RuntimeException。如果指定的文件是一个已存在的目录,或者由于其他原因不能打开文件,会抛出异常FileNotFoundException,它是IOException的一个子类。

    我们看一段简单的代码,将字符串"hello, 123, 老马"写到文件hello.txt中:

    OutputStream output =  new FileOutputStream("hello.txt");
    try{
        String data = "hello, 123, 老马";
        byte[] bytes = data.getBytes(Charset.forName("UTF-8"));
        output.write(bytes);
    }finally{
        output.close();
    }

    OutputStream只能以byte或byte数组写文件,为了写字符串,我们调用String的getBytes方法得到它的UTF-8编码的字节数组,再调用write方法,写的过程放在try语句内,在finally语句中调用close方法。

    FileOutputStream还有两个额外的方法:

    public FileChannel getChannel()
    public final FileDescriptor getFD()

    FileChannel定义在java.nio中,表示文件通道概念,我们不会深入介绍通道,但内存映射文件方法定义在FileChannel中,我们会在后续章节介绍。FileDescriptor表示文件描述符,它与操作系统的一些文件内存结构相连,在大部分情况下,我们不会用到它,不过它有一个方法sync:

    public native void sync() throws SyncFailedException;

    这是一个本地方法,它会确保将操作系统缓冲的数据写到硬盘上。注意与OutputStream的flush方法相区别,flush只能将应用程序缓冲的数据写到操作系统,sync则确保数据写到硬盘,不过一般情况下,我们并不需要手工调用它,只要操作系统和硬件设备没问题,数据迟早会写入,但在一定特定情况下,一定需要确保数据写入硬盘,则可以调用该方法。

    FileInputStream

    FileInputStream的主要构造方法有:

    public FileInputStream(String name) throws FileNotFoundException
    public FileInputStream(File file) throws FileNotFoundException

    参数与FileOutputStream类似,可以是文件路径或File对象,但必须是一个已存在的文件,不能是目录。new一个FileInputStream对象也会实际打开文件,操作系统会分配相关资源,如果文件不存在,会抛出异常FileNotFoundException,如果当前用户没有读的权限,会抛出异常SecurityException。

    我们看一段简单的代码,将上面写入的文件"hello.txt"读到内存并输出:

    InputStream input = new FileInputStream("hello.txt");
    try{
        byte[] buf = new byte[1024];
        int bytesRead = input.read(buf);    
        String data = new String(buf, 0, bytesRead, "UTF-8");
        System.out.println(data);
    }finally{
        input.close();
    }

     

    读入到的是byte数组,我们使用String的带编码参数的构造方法将其转换为了String。这段代码假定一次read调用就读到了所有内容,且假定字节长度不超过1024。为了确保读到所有内容,可以逐个字节读取直到文件结束:

    int b = -1;
    int bytesRead = 0;
    while((b=input.read())!=-1){
        buf[bytesRead++] = (byte)b;
    }

    在没有缓冲的情况下逐个字节读取性能很低,可以使用批量读入且确保读到文件结尾,如下所示:

    byte[] buf = new byte[1024];
    int off = 0;
    int bytesRead = 0;
    while((bytesRead=input.read(buf, off, 1024-off ))!=-1){
        off += bytesRead;
    }    
    String data = new String(buf, 0, off, "UTF-8");

    不过,这还是假定文件内容长度不超过一个固定的大小1024。如果不确定文件内容的长度,不希望一次性分配过大的byte数组,又希望将文件内容全部读入,怎么做呢?可以借助ByteArrayOutputStream。

    ByteArrayInputStream/ByteArrayOutputStream

    ByteArrayOutputStream

    ByteArrayOutputStream的输出目标是一个byte数组,这个数组的长度是根据数据内容动态扩展的。它有两个构造方法:

    public ByteArrayOutputStream()
    public ByteArrayOutputStream(int size) 

    第二个构造方法中的size指定的就是初始的数组大小,如果没有指定,长度为32。在调用write方法的过程中,如果数组大小不够,会进行扩展,扩展策略同样是指数扩展,每次至少增加一倍。

    ByteArrayOutputStream有如下方法,可以方便的将数据转换为字节数组或字符串:

    public synchronized byte[] toByteArray()
    public synchronized String toString()
    public synchronized String toString(String charsetName)

    toString()方法使用系统默认编码。

    ByteArrayOutputStream中的数据也可以方便的写到另一个OutputStream:

    public synchronized void writeTo(OutputStream out) throws IOException

    ByteArrayOutputStream还有如下额外方法:

    public synchronized int size()
    public synchronized void reset()

    size返回当前写入的字节个数。reset重置字节个数为0,reset后,可以重用已分配的数组。

    使用ByteArrayOutputStream,我们可以改进上面的读文件代码,确保将所有文件内容读入:

    InputStream input = new FileInputStream("hello.txt");
    try{
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        int bytesRead = 0;
        while((bytesRead=input.read(buf))!=-1){
            output.write(buf, 0, bytesRead);
        }    
        String data = output.toString("UTF-8");
        System.out.println(data);
    }finally{
        input.close();
    }

    读入的数据先写入ByteArrayOutputStream中,读完后,再调用其toString方法获取完整数据。 

    ByteArrayInputStream

    ByteArrayInputStream将byte数组包装为一个输入流,是一种适配器模式,它的构造方法有:

    public ByteArrayInputStream(byte buf[])
    public ByteArrayInputStream(byte buf[], int offset, int length)

    第二个构造方法以buf中offset开始length个字节为背后的数据。ByteArrayInputStream的所有数据都在内存,支持mark/reset重复读取。

    为什么要将byte数组转换为InputStream呢?这与容器类中要将数组、单个元素转换为容器接口的原因是类似的,有很多代码是以InputStream/OutputSteam为参数构建的,它们构成了一个协作体系,将byte数组转换为InputStream可以方便的参与这种体系,复用代码。

    DataInputStream/DataOutputStream

    上面介绍的类都只能以字节为单位读写,如何以其他类型读写呢?比如int, double。可以使用DataInputStream/DataOutputStream,它们都是装饰类。

    DataOutputStream

    DataOutputStream是装饰类基类FilterOutputStream的子类,FilterOutputStream是OutputStream的子类,它的构造方法是:

    public FilterOutputStream(OutputStream out)

    它接受一个已有的OutputStream,基本上将所有操作都代理给了它。

    DataOutputStream实现了DataOutput接口,可以以各种基本类型和字符串写入数据,部分方法如下:

    void writeBoolean(boolean v) throws IOException;
    void writeInt(int v) throws IOException;
    void writeDouble(double v) throws IOException;
    void writeUTF(String s) throws IOException;

    在写入时,DataOutputStream会将这些类型的数据转换为其对应的二进制字节,比如:

    • writeBoolean: 写入一个字节,如果值为true,则写入1,否则0
    • writeInt: 写入四个字节,最高位字节先写入,最低位最后写入
    • writeUTF: 将字符串的UTF-8编码字节写入,这个编码格式与标准的UTF-8编码略有不同,不过,我们不用关心这个细节。 

    与FilterOutputStream一样,DataOutputStream的构造方法也是接受一个已有的OutputStream:

    public DataOutputStream(OutputStream out)

    我们来看一个例子,保存一个学生列表到文件中,学生类的定义为:

    class Student {
        String name;
        int age;
        double score;
        
        public Student(String name, int age, double score) {
             ...
        }
        ...
    }    

    我们省略了构造方法和getter/setter方法,学生列表内容为:

    List<Student> students = Arrays.asList(new Student[]{
            new Student("张三", 18, 80.9d),
            new Student("李四", 17, 67.5d)
    });

    将该列表内容写到文件students.dat中的代码可以为:

    public static void writeStudents(List<Student> students) throws IOException{
        DataOutputStream output = new DataOutputStream(
                new FileOutputStream("students.dat"));
        try{
            output.writeInt(students.size());
            for(Student s : students){
                output.writeUTF(s.getName());
                output.writeInt(s.getAge());
                output.writeDouble(s.getScore());
            }
        }finally{
            output.close();
        }
    }

     

    我们先写了列表的长度,然后针对每个学生、每个字段,根据其类型调用了相应的write方法。

    DataInputStream

    DataInputStream是装饰类基类FilterInputStream的子类,FilterInputStream是InputStream的子类。

    DataInputStream实现了DataInput接口,可以以各种基本类型和字符串读取数据,部分方法如下:

    boolean readBoolean() throws IOException;
    int readInt() throws IOException;
    double readDouble() throws IOException;
    String readUTF() throws IOException;

    在读取时,DataInputStream会先按字节读进来,然后转换为对应的类型。

    DataInputStream的构造方法接受一个InputStream:

    public DataInputStream(InputStream in)

    还是以上面的学生列表为例,我们来看怎么从文件中读进来:

    public static List<Student> readStudents() throws IOException{
        DataInputStream input = new DataInputStream(
                new FileInputStream("students.dat"));
        try{
            int size = input.readInt();
            List<Student> students = new ArrayList<Student>(size);
            for(int i=0; i<size; i++){
                Student s = new Student();
                s.setName(input.readUTF());
                s.setAge(input.readInt());
                s.setScore(input.readDouble());
                students.add(s);
            }
            return students;
        }finally{
            input.close();
        }
    }

     

    基本是写的逆过程,代码比较简单,就不赘述了。

    使用DataInputStream/DataOutputStream读写对象,非常灵活,但比较麻烦,所以Java提供了序列化机制,我们在后续章节介绍。

    BufferedInputStream/BufferedOutputStream

    FileInputStream/FileOutputStream是没有缓冲的,按单个字节读写时性能比较低,虽然可以按字节数组读取以提高性能,但有时必须要按字节读写,比如上面的DataInputStream/DataOutputStream,它们包装了文件流,内部会调用文件流的单字节读写方法。怎么解决这个问题呢?方法是将文件流包装到缓冲流中。

    BufferedInputStream内部有个字节数组作为缓冲区,读取时,先从这个缓冲区读,缓冲区读完了再调用包装的流读,它的构造方法有两个:

    public BufferedInputStream(InputStream in)
    public BufferedInputStream(InputStream in, int size)

    size表示缓冲区大小,如果没有,默认值为8192。

    除了提高性能,BufferedInputStream也支持mark/reset,可以重复读取。

    与BufferedInputStream类似,BufferedOutputStream的构造方法也有两个,默认的缓冲区大小也是8192,它的flush方法会将缓冲区的内容写到包装的流中。

    在使用FileInputStream/FileOutputStream时,应该几乎总是在它的外面包上对应的缓冲类,如下所示:

    InputStream input = new BufferedInputStream(new FileInputStream("hello.txt"));
    OutputStream output =  new BufferedOutputStream(new FileOutputStream("hello.txt"));

    再比如:

    DataOutputStream output = new DataOutputStream(
            new BufferedOutputStream(new FileOutputStream("students.dat")));
    DataInputStream input = new DataInputStream(
            new BufferedInputStream(new FileInputStream("students.dat")));    

    实用方法

    可以看出,即使只是按二进制字节读写流,Java也包括了很多的类,虽然很灵活,但对于一些简单的需求,却需要写很多代码,实际开发中,经常需要将一些常用功能进行封装,提供更为简单的接口。下面我们提供一些实用方法,以供参考。

    拷贝

    拷贝输入流的内容到输出流,代码为:

    public static void copy(InputStream input,
            OutputStream output) throws IOException{
        byte[] buf = new byte[4096];
        int bytesRead = 0;
        while((bytesRead = input.read(buf))!=-1){
            output.write(buf, 0, bytesRead);
        }
    }    

     

    将文件读入字节数组

    代码为:

    public static byte[] readFileToByteArray(String fileName) throws IOException{
        InputStream input = new FileInputStream(fileName);
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        try{
            copy(input, output);
            return output.toByteArray();
        }finally{
            input.close();
        }
    }

    这个方法调用了上面的拷贝方法。

    将字节数组写到文件

    public static void writeByteArrayToFile(String fileName,
            byte[] data) throws IOException{
        OutputStream output = new FileOutputStream(fileName);
        try{
            output.write(data);
        }finally{
            output.close();    
        }
    }

    Apache有一个类库Commons IO,里面提供了很多简单易用的方法,实际开发中,可以考虑使用。

    小结

    本节我们介绍了如何在Java中以二进制字节的方式读写文件,介绍了主要的流。

    • InputStream/OutputStream:是抽象基类,有很多面向流的代码,以它们为参数,比如本节介绍的copy方法。
    • FileInputStream/FileOutputStream:流的源和目的地是文件。
    • ByteArrayInputStream/ByteArrayOutputStream:源和目的地是字节数组,作为输入相当于是适配器,作为输出封装了动态数组,便于使用。
    • DataInputStream/DataOutputStream:装饰类,按基本类型和字符串读写流。
    • BufferedInputStream/BufferedOutputStream:装饰类,提供缓冲,FileInputStream/FileOutputStream一般总是应该用该类装饰。

    最后,我们提供了一些实用方法,以方便常见的操作,在实际开发中,可以考虑使用专门的类库如Apache Commons IO。

    本节介绍的流不适用于处理文本文件,比如,不能按行处理,没有编码的概念,下一节,就让我们来看文本文件和字符流。

    原文:laoma

    展开全文
  • 二进制流和文本区别

    千次阅读 2017-07-06 11:03:39
    在C中引入了(stream)的概念。它将数据的输入输出看作是数据的流入和流出,这样不管是磁盘文件或者是物理设备(打印机、显示器、键盘等),都可看作一种的源和目的,视他们为同一种东西,而不管其具体的物理结构,即...
  • 如何将字符串转化成二进制流的字符串了?
  • 把对象序列化为二进制字节数组和把二进制字节数组反序列化为对象的时间加起来,时间越少,性能越高。 Protocal BUffers性能优异,跨平台,需要些proto文件,无法直接用java对象。Hessian效率稍低,支持语言良好,...
  • 如题,想把二进制字节流转成jpg图片,一个像素灰度对应一个字节,最后输出到指定路径。求教大神如何实现?
  • 用Visual Studio查看图片的二进制流

    千次阅读 2018-11-06 20:01:49
    有时候我们需要查看图片的二进制流,比如做iOS马甲包的时候,为了让图片的md5发生变化,又要保持图片本身不变,我们会在后面写入空行之类的操作(或者写0)。怎么以二进制流的方式查看一张图片呢?   打开visual ...
  • 1、class文件:里面存放了许多16进制的字节(码)(如二进制字节码可表示为:010101101),如下图所示;但是这个文件,是二进制的,例如图形文件等都属于二进制文件。 2、javap -v class文件名 &gt; 输出文件名...
  • 对于: ``` cap = cv2.VideoCapture(0) while(True): ret, frame = cap.read() ``` ...目的是因为使用的一个API需要传入二进制文件,但是摄像头用opencv打开后每帧的对象并不是二进制的。
  • PHP将二进制字节流转成中文字符串

    千次阅读 2016-09-30 13:14:15
    最近在开发YII2中文验证码功能,需要一种成语的验证码,然后我用C++生成了一些成语,最终产生二进制文件,按GBK编码保存,一个汉字两字节。 最终是需要PHP读取二进制文件的,在读取二进制文件时碰到问题,需要将...
  • 首先在物理上文本文件和二进制... 对于读到的二进制流的解释方式可以分为按文本方式和按二进制方式打开,按文本方式比如ASCII码形式解释,就把读到的二进制流每8比特8比特的翻译,存储是反过程,如果按照二进制
  • 字节数据转换成十六进制字符串

    千次阅读 2015-11-21 16:44:58
    字节,8个二进制位,而一个十六进制字符0-F,需要四个二进制位进行表示,因此我们可以直接将一个字节拆分成两个部分(前后两个四位二进制位),然后分别将四个二进制位转换成一个十六进制字符。 具体实现如下:...
  • 文件转为二进制流保存在数据库

    千次阅读 2019-03-08 17:54:15
    **文件不建议保存在数据库,保存URL会更好,文件这种让文件系统去操作不是更好吗?我也不知道当时为什么会整理这样一篇文章。现在来说,文件不要存在数据库,存URL就好!!!**
  • 最近学C++的时候想了个问题,能否将文件或图片转换成二进制的字符串, 然后在需要的时候将二进制数据转换成文件能,这样就相当于模拟了文件上传的过程, 希望有源码配上详细的注释
  • python写入二进制文件

    万次阅读 2018-04-16 21:09:06
    import struct list_dec = [1, 2, 3, 4, 53, 100, 220, 244, 255] with open('hexBin.bin', 'wb')as fp: for x in list_dec: a = struct.pack('B', x) fp.write(a) print('done')简言之就是用struct...
  • 二进制文件和文本文件的区别: http://blog.csdn.net/qingyue_bao/article/details/6590110 ... tcp流传输与二进制流区别: http://blog.csdn.net/qingyue_bao/article/details/
  • 字节流转换成十六进制字符串

    千次阅读 2016-09-19 18:54:47
    字节流转换成十六进制字符串
  • php读取二进制流

    万次阅读 2014-01-22 13:58:21
    将php数据转换为二进制数据 string pack ( string $format [, mixed $args [, mixed $...]] ) 将二进制数据转换为php数据 array unpack ( string $format, string $data ) $format: a – NUL-padded string ...
1 2 3 4 5 ... 20
收藏数 176,345
精华内容 70,538
关键字:

二进制字节流