Java IO流实践

  7 分钟   8186 字    |    

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类:

  • 字节缓冲流BufferedInputStreamBufferedOutputStream
//构造方式一: 创建字节缓冲输入流【但是开发中一般常用下面的格式申明】
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"));
  • 字符缓冲流BufferedReaderBufferedWriter
// 创建字符缓冲输入流
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

~  ~  The   End  ~  ~


 赏 
感谢您的支持,我会继续努力哒!
支付宝收款码
tips
文章二维码 分类标签:技术java
文章标题: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)

相关推荐

热门推荐

(*) 8 + 3 =
快来做第一个评论的人吧~