Java IO流实践
Java IO流实践
初识IO
IO,即in和out,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。
流(Stream),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。
一般来说关于流的特性有下面几点:
- 先进先出:最先写入输出流的数据最先被输入流读取到。
- 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)
- 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。
传输方式
传输方式有两种,字节和字符
字节(byte)是计算机中用来表示存储容量的一个计量单位,通常情况下,一个字节有 8 位(bit)
字符(char)可以是计算机中使用的字母、数字、和符号
通常来说,一个字母或者一个字符占用一个字节,一个word占用两个字节
字节流用来处理二进制文件,比如说图片、MP3、视频;字符流用来处理文本文件,文本文件可以看作是一种特殊的二进制文件,只不过经过了编码,便于人们阅读。
字节流可以处理一切文件,而字符流只能处理文本
Java IO类
抽象类
核心 4 个抽象类:InputStream、OutputStream、Reader、Writer
InputStream 类
int read()
:读取数据int read(byte b[], int off, int len)
:从第 off 位置开始读,读取 len 长度的字节,然后放入数组 b 中long skip(long n)
:跳过指定个数的字节int available()
:返回可读的字节数void close()
:关闭流,释放资源
OutputStream 类
void write(int b)
: 写入一个字节,虽然参数是一个 int 类型,但只有低 8 位才会写入,高 24 位会舍弃(这块后面再讲)void write(byte b[], int off, int len)
: 将数组 b 中的从 off 位置开始,长度为 len 的字节写入void flush()
: 强制刷新,将缓冲区的数据写入void close()
:关闭流
Reader 类
int read()
:读取单个字符int read(char cbuf[], int off, int len)
:从第 off 位置开始读,读取 len 长度的字符,然后放入数组 b 中long skip(long n)
:跳过指定个数的字符int ready()
:是否可以读了void close()
:关闭流,释放资源
Writer 类
void write(int c)
: 写入一个字符void write( char cbuf[], int off, int len)
: 将数组 cbuf 中的从 off 位置开始,长度为 len 的字符写入void flush()
: 强制刷新,将缓冲区的数据写入void close()
:关闭流
字节流和字符流的区别:
- 字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。
- 字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。
缓冲流
缓冲流的基本原理:
1、使用底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内。
2、通过缓冲区的read()方法从缓冲区获取具体的字符数据,这样就提高了效率。
3、用read方法读取字符数据,并存储到另外一个容器中,直到读取到了换行符时,再将另一个容器临时存储的数据转成字符串返回,也就是readLine()的功能。
在创建缓冲流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写效率。
缓冲书写格式为BufferedXxx
,按照数据类型分为 2类:
- 字节缓冲流:
BufferedInputStream
,BufferedOutputStream
//构造方式一: 创建字节缓冲输入流【但是开发中一般常用下面的格式申明】
FileInputStream fps = new FileInputStream(b.txt);
BufferedInputStream bis = new BufferedInputStream(fps)
//构造方式一: 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("b.txt"));
///构造方式二: 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
- 字符缓冲流:
BufferedReader
,BufferedWriter
// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("b.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
实践
使用Socket开启tcp服务器,接收输入流,并从输入流里截取与转化为16进制数据
public class ServerReceiveThread implements Runnable {
private KafkaTemplate<Object, Object> template = SpringUtilsAuTo.getBean(KafkaTemplate.class);
private Socket socket;
public ServerReceiveThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//读取输入流
BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
//计算正确率
int total = 0;
int ret = 0;
//输入缓冲区
byte[] bytes = new byte[1024 * 2];
int len;
byte[] totalBytes = new byte[]{};
int totalLength = 0;
//循环读取
while ((len = in.read(bytes)) != -1) {
//1. 将读取的数据和上一次遗留的数据拼起来
int tempLength = totalLength;
totalLength = len + totalLength;
byte[] tempBytes = totalBytes;
totalBytes = new byte[totalLength];
System.arraycopy(tempBytes, 0, totalBytes, 0, tempLength);
System.arraycopy(bytes, 0, totalBytes, tempLength, len);
while (totalLength > 30) {
//1.计算数据体长度
int dataLength = StreamUtils.getShort4(totalBytes);
//2.获取的数据小于总长度,则出现半包,再次获取数据连接
if (totalLength < dataLength) {
break;
}
int contentLength = dataLength;
//3. 将数据体的指定长度的数据取出则为应用数据
byte[] contentBytes = new byte[contentLength];
System.arraycopy(totalBytes, 0, contentBytes, 0, contentLength);
//字节流转16进制字符串
String contentHex = StreamUtils.bytesToHexString(contentBytes);
String correct = "error";
try {
//校验
String bcc = StreamUtils.getBCC(contentHex.substring(4, contentHex.length() - 2));
total += 1;
if (bcc.equals(contentHex.substring(contentHex.length() - 2))) {
correct = "correct";
ret += 1;
}
log.info("[" + total + "]: contentLength: " +
contentLength + ", BCC: " + correct +
", accuracy: " + (float) ret / total);
} catch (Exception e) {
//e.printStackTrace();
}
//送入kafka
if ("correct".equals(correct)) {
template.send("dec_hyd_sys_2301", contentHex);
}
//4. 去掉已读取的数据
totalLength -= contentBytes.length;
byte[] leftBytes = new byte[totalLength];
System.arraycopy(totalBytes, contentLength, leftBytes, 0, totalLength);
totalBytes = leftBytes;
}
}
in.close();
socket.close();
log.info("关闭客户端[{}]", socket.getRemoteSocketAddress());
} catch (Exception e) {
log.info("接收数据异常socket关闭");
e.printStackTrace();
}
}
}
参考
[ 1 ] https://juejin.cn/post/6971766563804282887
[ 2 ] https://github.com/itwanger/toBeBetterJavaer
文章标题:Java IO流实践
文章链接:http://120.46.217.131:82/archives/49/
最后编辑:2023 年 1 月 12 日 09:30 By Yang
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)