连续使用一个字节数组读取文件
在使用java的数组来实现连续读取数据的时候,遇到这样一个问题。
我通过一个8字节数组来通过FileOutputStream.read()
读取文件中的数据,所以我每次最多读8个字节,我文件中的数据如下,总共47个字符也就是47个字节,所以需要读6次;
dshakjdhjksahklfdahkjhkjf hdljksahkfjlkjhadskjh
1 | public static void read(String path) { |
当我每读一次就把当前读到数组里的数据输出时,我发现数据与我文件中的不匹配。读取的结果如下:
1 | 1: d 2: s 3: h 4: a 5: k 6: j 7: d 8: h |
我们可以看到输出了48个字节多输出了一个字节,思考了一下我们可以想到,当我们最后一次读取文件数据时,如果这时候我们可以读取的数据已经不足8个字节,那么会怎么样呢,那么我们可以想到这最后一个字节可能来自上次8个字节的最后一个字节即第40个字节,每次使用FileInputStream.read()
去读取数据,当前读取的数据都会把之前读取的数据给覆盖掉。所以我们每次读取之后,都要先把byte数组清空之后再进行下次读取;可以使用java.util.Arrays.fill()
来实现;
一个字节的表示
首先对于一个字节来说,表示有符号数时,范围是-128~127
,其中-128
的表示为10000000
;正数为原码形式存在,负数为补码形式存在;比如对于一个字节来说,如果向计算机写入-1
,它是一个有符号的负数,所以计算机会计算它的补码,从而转换为11111111
;而对于128~255
之间的数,计算机会自动视其为无符号数,那就以无符号原码表示,如0xff
即255,就是11111111
;
所以有时候,我们就会遇到,255和-1都表示为11111111
的情况;
BigInteger.toByteArray()怎么实现的
在网上找了一个说的比较好的解答,也有了自己的理解。
下面是一个int的编码实例,其中需要注意二进制的4位表示一个十六进制数字,负数二进制开始为1,正数开始于0;
1 | -2147483648 is encoded as 1000 0000 0000 0000 0000 0000 0000 0000 or 0x80000000. |
java只是把,每个8位块中现有的位拷贝到目标数组中的问题。一个字节即是一个8位的块.
我们将一个BigInteger转换为二进制补码,我们想要的只是它的后面的有效的位,而不包含前面的无数的前导0和前导1;之后每8位的块即是一个字节;
下面用数组里的十六进制来表示BigInteger;
1 | 0 [0x00000000] |
之后分割、拿出其中有效的byte,即以每八位分割一次;
1 | 8589934592 [0x00 00 00 02, 0x00 00 00 00] |
- 前面的三个00都可以被忽略,因为第一个有效数字之前有25个0;
- 然后将这些二进制位拷贝到字节中即可。
对于官方的文档,其中有这样一句话,toByteArray()这个方法返回一个数组,这个数组包含可以代表这个BigInteger最小数量的字节,其中包含至少一个符号位。
符号位是什么意思呢。对于-128~127
之间的数组,我们都可以用一个字节来表示,而对于128~255
之间的数呢,比如255与-1,虽然二进制表示相同,但他们终究是不同的,所以这个方法考虑到这个问题,对于255就额外加了一个0x00表示符号位(如果第一个字节的大小大于0x7f,就需要在最前面加上一个0x00);或者可以这样想,对于有符号数来说,128~255
之间的数,一个字节是表示不了的,所以采用两个字节来表示,只不过最左边的那个字节是0x00;比如对于下面的两个byte数组:
1 | 255 [0x00, 0xff] |
这个问题需要特别注意;
byte数组转BigInteger
BigInteger有这样一个构造函数:public BigInteger(byte[] val)
,它的参数是byte数组;对于[0x00,0xff]
得到的BigInteger的到的结果是255,对于[0xff]
的结果是-1;
BigInteger还有这样一个构造函数:public BigInteger(int signum,byte[] magnitude)
,其中多加了一个signum参数代表你的BigInteger的正负,为-1代表负数,为1代表整数,为0代表byte数组表示的数一定是0(即这个byte数组仅包括字节0x00),如果byte数组表示的数不为0,会报错。
实践后发现,可能是这样的,如果signum为0,则在byte数组最前面加上一个0x00,然后反转上面toByteArray()的操作,得到最后的结果,如果signum为1,前面计算的结果上加上一个负号。
1 | public class Main { |