Java IO(十一) DataInputStream 和 DataOutputStream
Java IO(十一) DataInputStream 和 DataOutputStream
一、介绍
DataInputStream 和 DataOutputStream 是数据字节流,分别继承自 FilterInputStream 和 FilterOutputStream 和 实现了 DataInput 和 DataOutput。用来装饰其他的字节流。
- DataInputStream:数据字节输入流,用来装饰其他的输入流,允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用 DataOutputStream (数据字节输出流)写入由 DataInputStream (数据字节输入流)读取的数据。
- DataOutputStream :数据字节输出流,用来装饰其他输出流,将 DataOutputStream 和 DataInputStream 输入流配合使用,允许应用程序以与机器无关方式从底层输入流中读写基本 Java 数据类型。
二、构造方法
(一)、DataInputStream
public DataInputStream(InputStream in) //创建使用指定的底层 InputStream 的输入数据流
(二)、DataOutputStream
public DataOutputStream(OutputStream out) // 创建一个使用指定底层 OutputStream 的数据输出流,以将数据写入指定的底层输出流。
三、常用API
(一)、DataInputStream

(二)、DataOutputStream

四、DataInputStream 和 DataOutputStream 的编码问题
DataInputStream 和 DataOutputStream 关于编码有关的方法,分别是 String readUTF() 和 void writeUTF(String str)。下面就这个两个方法说明一下编码问题
(一)、DataOutputStream 的 void writeUTF(String str)
DataOutputStream 的 writeUTF 方法是将以 UTF-8 的编码格式的数据写入底层输出流中。具体流程如下:
①、定义一个 strlen 变量,存储字符串的长度 int strlen = str.length();
②、通过 for 循环遍历 str ,根据c的大小决定存储长度utflen的大小,最大65535字节,也就是64kb,超出最大字节会抛出 UTFDataFormatException 异常。
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; // 两字节
}
}
③、定义一个byte数组类型的变量 bytearr,判断该数据输出流是否是 DataOutputStream,若是则: 设置bytearr的大小。+2是需要在 bytearr 前两字节中存储数据长度 utflen。
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];
}
④、根据字符串的字符的字节数分别放入 bytearr 数组中
int i=0;
// 如果是ascii码就直接存在 bytearr 数组中
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);
if ((c >= 0x0001) && (c <= 0x007F)) {
// 如果是单字节
bytearr[count++] = (byte) c;
} else if (c > 0x07FF) {
// 三字节
bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
} else {
// 两字节
bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
}
}
out.write(bytearr, 0, utflen+2);
return utflen + 2; // 返回 bytearr 数组大小
源码基于JDK1.8,上面只贴出主要源码,详细源码自行查看 DataOutputStream 类。
(二)、DataInputStream 的 String readUTF()
上面知道了写入的规则,DataInputStream 的 String readUTF() 读取的规则与写入的规则相反。
①、定义 utflen 变量读取输入流中UTF-8数据的长度: int utflen = in.readUnsignedShort();
②、创建字符数组 bytearr 和 字节数组 chararr
byte[] bytearr = null;
char[] chararr = null;
③、判断该数据输入流是否是 DataInputStream ,若是,则:
- 设置字节数组bytearr = "数据输入流"的成员bytearr
- 设置字符数组chararr = "数据输入流"的成员chararr
否则,新建数组 bytearr 和 chararr。
if (in instanceof DataInputStream) {
DataInputStream dis = (DataInputStream)in;
if (dis.bytearr.length < utflen){
dis.bytearr = new byte[utflen*2];
dis.chararr = new char[utflen*2];
}
chararr = dis.chararr;
bytearr = dis.bytearr;
} else {
bytearr = new byte[utflen];
chararr = new char[utflen];
}
④、将UTF-8数据全部读取到字节数组 bytearr 中通过 readFully() 方法: in.readFully(bytearr, 0, utflen);
⑤、对UTF-8中单字节数据进行预处理,UTF-8的数据是变长的,可以是1-4个字节;在readUTF()中,我们最终是将全部的UTF-8数据保存到字符数组(而不是字节数组)中,再将其转换为String字符串。
由于UTF-8的单字节和ASCII相同,所以这里就将它们进行预处理,直接保存到字符数组 chararr 中。对于其它的UTF-8数据,则在后面进行处理。
while (count < utflen) {
c = (int) bytearr[count] & 0xff; // 将每字节转成 int 值
if (c > 127) break; // UTF-8单子节数据不会超过127,超过则退出
count++;
chararr[chararr_count++]=(char)c;// 将int转成char存入字符数组
}
⑥、对预处理后的数据进行再处理
while (count < utflen) {
c = (int) bytearr[count] & 0xff; // 将字节有 byte 转成 int
switch (c >> 4) { // 将 int 类型左移 4 位
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
/* 若UTF-8是单字节数据,则 bytearr[count] 的格式:0xxxxxxx,对应 int 类型的字符 c 取值范围:0-7 */
count++;
chararr[chararr_count++]=(char)c;
break;
case 12: case 13:
/* 若UTF-8是两字节数据,则 bytearr [count]的格式:110x xxxx 10xx xxxx,对应 int 类型的字符 c 的取值范围:12-13*/
count += 2;
if (count > utflen)
throw new UTFDataFormatException("malformed input: partial character at end");
char2 = (int) bytearr[count-1];
if ((char2 & 0xC0) != 0x80)
throw new UTFDataFormatException("malformed input around byte " + count);
chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |(char2 & 0x3F));
break;
case 14:
/* 若UTF-8是三字节数据,则 bytearr[count] 的格式:1110 xxxx 10xx xxxx 10xx xxxx,对应 int 类型的字符 c 的取值范围是:14 */
count += 3;
if (count > utflen)
throw new UTFDataFormatException("malformed input: partial character at end");
char2 = (int) bytearr[count-2];
char3 = (int) bytearr[count-1];
if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
throw new UTFDataFormatException("malformed input around byte " + (count-1));
chararr[chararr_count++]=(char)(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
break;
default:
/* 若UTF-8是四字节的数据,则bytearr[count] 的格式:10xx xxxx,1111 xxxx,对应 int 类型的字符 c 的取值范围是:15 */
throw new UTFDataFormatException("malformed input around byte " + count);
}
}
⑦、将字符数组 chararr 转成 String类型返回: return new String(chararr, 0, chararr_count);
五、实例
public static void main(String[] args) {
write();
read();
}
/**
* DataInputStream 测试
*/
public static void read() {
DataInputStream in = null;
try {
in = new DataInputStream(new FileInputStream("data.txt"));
System.out.println("readUTF : " + in.readUTF());
System.out.println("read : " + (char)in.read());
System.out.println("readBoolean : " + in.readBoolean());
System.out.println("readDouble : " + in.readDouble());
System.out.println("readFloat : " + in.readFloat());
System.out.println("readByte : " + in.readByte());
System.out.println("readChar : " + in.readChar());
System.out.println("readLong : " + in.readLong());
System.out.println("readLong : " + in.readLong());
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if (in != null)
in.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* DataOutputStream 测试
*/
public static void write() {
DataOutputStream out = null;
try {
out = new DataOutputStream(new FileOutputStream("data.txt"));
out.writeUTF("Java IO 入门");
out.write('A');
out.writeBoolean(true);
out.writeDouble(3.14D);
out.writeFloat(0.69F);
out.write("abcdefg".getBytes());
out.writeByte(99);
out.writeChars("Hello World");
out.writeLong(46L);
out.writeBytes("data input stream");
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if (out != null)
out.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
Java IO(十一) DataInputStream 和 DataOutputStream的更多相关文章
- java io系列15之 DataOutputStream(数据输出流)的认知、源码和示例
本章介绍DataOutputStream.我们先对DataOutputStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解. 转载请注明出处:http://www.cnblog ...
- Java之IO(四)DataInputStream和DataOutputStream
转载请注明源出处:http://www.cnblogs.com/lighten/p/6986155.html 1.前言 DataInputStream和DataOutputStream分别继承了Fil ...
- 系统学习 Java IO (十一)----打印流 PrintStream
目录:系统学习 Java IO---- 目录,概览 PrintStream 类可以将格式化数据写入底层 OutputStream 或者直接写入 File 对象. PrintStream 类可以格式化基 ...
- java io系列14之 DataInputStream(数据输入流)的认知、源码和示例
本章介绍DataInputStream.我们先对DataInputStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解. 转载请注明出处:http://www.cnblogs. ...
- java io系列16之 PrintStream(打印输出流)详解
本章介绍PrintStream以及 它与DataOutputStream的区别.我们先对PrintStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解. 转载请注明出处:htt ...
- java io系列01之 "目录"
java io 系列目录如下: 01. java io系列01之 "目录" 02. java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括 ...
- java io系列
java io系列01之 "目录" java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括InputStream) java io系列03之 ...
- 系统学习 Java IO ---- 目录,概览
Java IO 类的系统教程,原创.主要参考自英文教程 Java IO Tutorial 和 Java Doc. http://tutorials.jenkov.com/java-io/index.h ...
- java io流 数据流 DataInputStream、DataOutputStream、ByteArrayInputStream、ByteArrayOutputStream
例子程序: package io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import ...
随机推荐
- Android控件重叠显示小记
方案一 利用布局控件显示优先级 在xml中RelativeLayout,FrameLayout,靠后的控件显示在上层. 利用margin属性 margin属性可以控制控件间的距离,属性值为正值时,越大 ...
- 前端——localStorage详细总结
一.localStorage简介: 在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cooki ...
- CSS的基本语法及页面引用
CSS的基本语法及页面引用 CSS基本语法 CSS的定义方法是: 选择器 { 属性:值; 属性:值; 属性:值;} 选择器是将样式和页面元素关联起来的名称,属性是希望设置的样式属性每个属性有一个或多个 ...
- 201771030125-王英雪 实验一 软件工程准备一<构建之法与博客首秀>
项目 内容 班级博客 点我呀! 作业要求 看这里! 课程学习目标 提出三个问题并以写博客的形式记录下来 参考文献 现代软件工程讲义 三个问题 问题一:软件工程究竟是什么? 在现代软件工程讲义一书中给出 ...
- 【华为云技术分享】智能诊断和优化,华为云DAS服务云DBA平台让您无忧运维
摘要:随着时代的发展,传统的“人工”运维方式,已经逐渐跟不上企业业务发展的需要.如何更好的保证数据库系统的稳定性.安全性.完整性和高性能,实现运维工具化.产品化.自助化.自动化,是当前数据管理和运维面 ...
- Linux 内核代码风格
文章目录 从编码风格错误开始 快速修改编码风格的工具 scripts/checkpatch.pl scripts/Lindent astyle Linux 内核代码风格 1) 缩进 2) 把长的行和字 ...
- 我的linux学习日记day4
一.打包压缩与搜索命令 1.tar命令 -c 创建压缩文件 -x 解开压缩文件 -t 查看压缩包里有哪些文件 -j 用bzip2来压缩或解压 -z 用gzip压缩或解压 -v 显示过程 -f 目标文件 ...
- 初试stm32嵌入式开发遇到的巨坑
开发板使用的是st官方的stm32F207ZG nucleo,遇到的问题是在keil中报错: no target connected 到网上找答案,都是说gpio口的问题,让按着reset键改debu ...
- Pytorch数据集读取
Pytorch中数据集读取 在机器学习中,有很多形式的数据,我们就以最常用的几种来看: 在Pytorch中,他自带了很多数据集,比如MNIST.CIFAR10等,这些自带的数据集获得和读取十分简便: ...
- 6.1Go方法
第六章 Go方法 在第三章中讲解了struct,面向对象编程OOP已经是一个编程范式了,Go语言同样支持OOP开发. 一个对象就是一个变量,在这个对象中包含了一些方法,一个方法是一个和特殊类型关联的函 ...