SpringBoot接入RS-232串口通讯实现数据交互

目录

一、什么是RS-232?

先看看硬件通讯接口长啥样

RS-232 

二、方案一

1.前期准备

a.配置 RXTX

1)下载 RXTX 包并解压

2)拷贝动态库到对应的jdk目录下

·Windows平台

·Linux平台

3)在工程根目录下创建 lib 文件夹(与src平级)将 RXTXcomm.jar 放入该文件夹中

4)在 pom.xml 中引入本地 jar 包依赖

 2、编写Java配置代码

三、方案二

1、前期准备

a.配置pom.xml文件

2、编写Java配置代码

四、其他的方法


📢📢📢📣📣📣
哈喽!大家好,我是「Leen」。刚工作几年,想和大家一同进步🤝🤝
一位上进心十足的Java博主!😜😜😜
喜欢尝试一些新鲜的东西,平时比较喜欢研究一些新鲜技术和一些自己没有掌握的技术领域。能用程序解决的坚决不手动解决😜😜😜

目前已涉足Java、Python、数据库(MySQL、pgsql、MongoDB、Oracle...)、Linux、HTML、VUE、PHP、C(了解不多,主要是嵌入式编程方向做了一些)...(还在不断地学习,扩展自己的见识和技术领域中),希望可以和各位大佬们一起进步,共同学习🤝🤝

✨ 如果有对【Java】,或者喜欢看一些【实操笔记】感兴趣的【小可爱】,欢迎关注我

❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️

今天给大家分享的是Java 连接 RS-232串口进行通讯,研究了好几个日夜,终于是走通了两条通道,实现了通讯,大家自己要实现这个这两个方案前需要硬件支持哦。

OK,接下来直接进入正题

一、什么是RS-232?

先看看硬件通讯接口长啥样

RS-232 

RS-232 是电子工业协会 (EIA) 定义的串行通信标准电气接口。RS-232 实际上有 3 种不同的风格(A、B 和 C),每种风格都为开和关电平定义了不同的电压范围。最常用的品种是RS-232C,它将标记(开)位定义为-3V至-12V之间的电压,将空格(关)位定义为+3V至+12V之间的电压。RS-232C 规范规定,这些信号在无法使用之前可以传播约 25 英尺(8 米)。只要波特率足够低,您通常可以发送比这更远的信号。

这一块,大家可以自行搜索去了解一下,这个地方我就不多介绍了

二、方案一

第一个方案,是原始方案,比较老的一种通讯方式,需要给jdk添加相应的ddl配置文件和驱动,并且现在这些配置文件不好找了(因为官网的配置地址找不到了,不存在),当然如果要是花点$,找到的可能性还是比较大的,反正我是没花钱找了一套配置哈哈哈

1.前期准备

a.配置 RXTX

1)下载 RXTX 包并解压

网址:http://fizzed.com/oss/rxtx-for-java

官网我找的时候暂时是没有的,可以等到后面的时候再去看看

2)拷贝动态库到对应的jdk目录下
·Windows平台

拷贝 rxtxSerial.dll —> <JAVA_HOME>\jre\bin
拷贝 rxtxParallel.dll —> <JAVA_HOME>\jre\bin

·Linux平台

拷贝 librxtxSerial.so —> <JAVA_HOME>/jre/lib/i386/
拷贝 librxtxParallel.so —> <JAVA_HOME>/jre/lib/i386/

3)在工程根目录下创建 lib 文件夹(与src平级)将 RXTXcomm.jar 放入该文件夹中

这个地方我建议是通过命令的方式将RXTXcomm.jar包导入到本地的Maven库中 

mvn install:install-file -Dfile="绝对路径/RXTXcomm.jar" -DgroupId="org.rxtx" -DartifactId="rxtx" -Dversion="2.2.0" -Dpackaging="jar"

当然也可以选择另外一种方式,直接导入

<dependency>
  <groupId>gnu.io</groupId>
  <artifactId>RXTXcomm</artifactId>
  <scope>system</scope>
  <systemPath>${project.basedir}/lib/RXTXcomm.jar</systemPath>
</dependency>
4)在 pom.xml 中引入本地 jar 包依赖

导入成功后,直接添加依赖即可

        <dependency>
            <groupId>org.rxtx</groupId>
            <artifactId>rxtx</artifactId>
            <version>2.2.0</version>
        </dependency>

 2、编写Java配置代码

package com.leen.test.test2024.test530;

import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;

/**
 * Author:Leen
 * Date: 2024/6/3 0003 22:45
 */

public class SerialCommSendData implements SerialPortEventListener {
    private SerialPort serialPort; // 串口对象
    private InputStream input; // 输入流,用于读取串口数据
    private OutputStream output; // 输出流,用于向串口写入数据
    private static final int TIME_OUT = 2000; // 端口打开的超时时间
    private static final int DATA_RATE = 9600; // 串口的波特率

    // 初始化串口
    public void initialize() {
        CommPortIdentifier portId = null;
        Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

        // 枚举系统中的所有端口并尝试找到指定的端口
        while (portEnum.hasMoreElements()) {
            CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
            if (currPortId.getName().equals("COM3")) { // 将这里的 "COM3" 修改为你的端口名
                portId = currPortId;
                break;
            }
        }

        if (portId == null) {
            System.out.println("Could not find COM port.");
            return;
        }

        try {
            // 打开串口,使用类名作为应用程序名
            serialPort = (SerialPort) portId.open(this.getClass().getName(), TIME_OUT);

            // 设置串口参数
            serialPort.setSerialPortParams(DATA_RATE,
                    SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1,
                    SerialPort.PARITY_NONE);

            // 打开输入输出流
            input = serialPort.getInputStream();
            output = serialPort.getOutputStream();

            // 添加事件监听器
            serialPort.addEventListener(this);
            serialPort.notifyOnDataAvailable(true);
        } catch (Exception e) {
            System.err.println(e.toString());
        }
    }

    // 关闭串口
    public synchronized void close() {
        if (serialPort != null) {
            serialPort.removeEventListener();
            serialPort.close();
        }
    }

    // 事件处理器,当串口有数据可用时触发
    @Override
    public synchronized void serialEvent(SerialPortEvent oEvent) {
        if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
            try {
                int available = input.available();
                byte[] chunk = new byte[available];
                input.read(chunk, 0, available);

                // 显示结果,具体字符编码依赖于系统设置
                System.out.print(new String(chunk));
//                processData(chunk); // 处理接收到的数据
            } catch (Exception e) {
                System.err.println(e.toString());
            }
        }
    }

    // 发送数据的方法
    public void sendData(String data) {
        try {
            output.write(data.getBytes());
            output.flush();
            System.out.println("Data sent: " + data);
        } catch (Exception e) {
            System.err.println(e.toString());
        }
    }

    // 处理接收到的数据的方法
    private void processData(byte[] data) {
        // 可以在这里处理接收到的数据
        String receivedData = new String(data);
        System.out.println("Received: " + receivedData);
    }

    // 主方法,程序入口
    public static void main(String[] args) {
        SerialCommSendData main = new SerialCommSendData();
        main.initialize(); // 初始化并打开串口
        System.out.println("Started");

        // 发送数据示例
        main.sendData("Hello ,I'm Leen ! I'd like to communicate with you");
        //测试接收数据
        byte[] data = "TEST communicate with RS-232".getBytes();
        main.processData(data);

        try {
            Thread.sleep(1000000); // 保持程序运行一段时间
        } catch (InterruptedException ie) {
            // 处理中断异常
        }

        main.close(); // 关闭串口
    }
}

启动main方法后,就可以和硬件串口进行通讯了

这个方法可以通讯,但是需要不适用于团队开发和切环境运行,只适合自己玩一下。咱们可以看看第二个方案

三、方案二

说实话,方案二操作起来非常的简单,唯二的难点之一是需要花时间找依赖;另一点就是正式通讯的时候封装通讯语言

1、前期准备

a.配置pom.xml文件

<dependency>
     <groupId>org.scream3r</groupId>
     <artifactId>jssc</artifactId>
     <version>2.8.0</version>
</dependency>

2、编写Java配置代码

package com.leen.test.test2024.test530;

import jssc.SerialPort;
import jssc.SerialPortException;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import org.apache.commons.lang3.StringUtils;

/**
 * Author:Leen
 * Date: 2024/6/4 0004 11:28
 */
public class SerialCommTwo {
    private SerialPort serialPort; // 串口对象

    private static SerialCommTwo instance; // 单例实例

    private StringBuilder receivedDataBuffer = new StringBuilder(); // 缓存接收到的数据


    // 私有构造函数
    private SerialCommTwo(String portName) {
        serialPort = new SerialPort(portName);
    }

    // 获取单例实例的方法
    public static SerialCommTwo getInstance(String portName) {
        if (instance == null) {
            instance = new SerialCommTwo(portName);
        }
        return instance;
    }

    // 初始化串口
    public void initialize() {
        try {
            serialPort.openPort(); // 打开串口
            serialPort.setParams(SerialPort.BAUDRATE_9600,
                    SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1,
                    SerialPort.PARITY_NONE); // 设置串口参数

            serialPort.addEventListener(new SerialPortEventListener() {
                public void serialEvent(SerialPortEvent event) {
                    if (event.isRXCHAR()) { // 如果串口有数据可用
                        try {
                            byte[] buffer = serialPort.readBytes(); // 读取数据
//                            System.out.print(new String(buffer)); // 打印数据
                            // 缓存接收到的数据
                            if (StringUtils.isNotEmpty(new String(buffer))) {
                                receivedDataBuffer.append(new String(buffer));
                            }
                            // 在这里处理接收到的完整报文,例如检查结束标志
                            processReceivedData();
                        } catch (SerialPortException e) {
                            e.printStackTrace(); // 捕捉异常
                        }
                    }
                }

                // 处理接收到的数据的方法
                private void processReceivedData() {
                    String receivedData = receivedDataBuffer.toString();

                    // 检查报文是否完整,例如检查结束标志ETX
                    if (receivedData.contains("\u0003")) { // ETX结束标志
                        System.out.println("Received complete message:\n" + receivedData);
                        // 提取第四行到结束标志中间的内容
                        String[] lines = receivedData.split("\r\n|\n");
                        if (lines.length >= 4) {
                            StringBuilder extractedData = new StringBuilder();
                            for (int i = 3; i < lines.length; i++) {
                                if (lines[i].contains("\u0003")) { // 如果包含结束标志,停止提取
                                    break;
                                }
                                extractedData.append(lines[i]).append("\n");
                            }
                            System.out.println("Extracted data: \n" + extractedData.toString());
                        }
                        // 处理完成的报文后,清空缓存
                        receivedDataBuffer.setLength(0);
                    }
                }
            });
        } catch (SerialPortException e) {
            e.printStackTrace(); // 捕捉异常
        }
    }

    // 发送数据的方法
    public void sendData(String data) {
        try {
            serialPort.writeBytes(data.getBytes()); // 发送数据
            System.out.println("Sent: " + data); // 打印已发送的数据
        } catch (SerialPortException e) {
            e.printStackTrace(); // 捕捉异常
        }
    }

    // 关闭串口
    public void close() {
        try {
            if (serialPort != null) {
                serialPort.removeEventListener(); // 移除事件监听器
                serialPort.closePort(); // 关闭串口
            }
        } catch (SerialPortException e) {
            e.printStackTrace(); // 捕捉异常
        }
    }

    public static void main(String[] args) {
        SerialCommTwo serialComm = SerialCommTwo.getInstance("COM3"); // 替换为你的串口名
        serialComm.initialize(); // 初始化串口
        System.out.println("Started");

        // 发送测试数据
        String startChar = "\u0001"; // SOH 开始字符
        String endChar = "\u0003"; // ETX 结束字符
        serialComm.sendData(startChar +
                "Hello ,I'm Leen ! I'd like to communicate with you \n" +
                endChar);

        try {
            Thread.sleep(1000000); // 保持程序运行一段时间以接收数据
        } catch (InterruptedException ie) {
            ie.printStackTrace(); // 捕捉异常
        }

        serialComm.close(); // 关闭串口
    }
}

 启动main方法后,就可以和硬件串口进行通讯了

四、其他的方法

这个方法在这里不适用,可能对于别的地方有适用的

package com.leen.test.test2024.test530;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * Author:Leen
 * Date: 2024/5/30 0030 21:01
 */
public class NPortClient {
    private static final String SERVER_IP = "192.168.1.100";  // NPort的IP地址
    private static final int SERVER_PORT = 4001;  // NPort的端口号

    public static void main(String[] args) {
        try (Socket socket = new Socket(SERVER_IP, SERVER_PORT);
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {

            System.out.println("Connected to NPort at " + SERVER_IP + ":" + SERVER_PORT);

            // 发送数据到NPort(可选)
            out.println("Hello NPort!");

            // 接收来自NPort的数据
            String response;
            while ((response = in.readLine()) != null) {
                System.out.println("Received from NPort: " + response);
                // 可以在这里处理接收到的数据
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package com.leen.test.test2024.test530;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * Author:Leen
 * Date: 2024/5/30 0030 21:05
 */
public class NPortClient2 {

    /**
     * 可放配置文件
     * server.ip=192.168.1.100
     * server.port=4001
     * reconnect.delay=5000  # 重连间隔时间(毫秒)
     */
    private static final Logger LOGGER = Logger.getLogger(NPortClient.class.getName());
    private static String serverIp;
    private static int serverPort;
    private static int reconnectDelay;

    private static boolean running = true;

    public static void main(String[] args) {
        loadConfig();
        ExecutorService executor = Executors.newFixedThreadPool(2);
        while (running) {
            try (Socket socket = new Socket(serverIp, serverPort);
                 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                 PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {

                LOGGER.info("Connected to NPort at " + serverIp + ":" + serverPort);

                // 启动接收数据线程
                executor.submit(() -> receiveData(in));

                // 发送数据示例(可根据需要调整)
                out.println("Hello NPort!");

            } catch (Exception e) {
                LOGGER.log(Level.WARNING, "Connection failed, retrying in " + reconnectDelay + "ms", e);
                try {
                    Thread.sleep(reconnectDelay);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
            }
        }
        executor.shutdown();
    }

    private static void receiveData(BufferedReader in) {
        String response;
        try {
            while ((response = in.readLine()) != null) {
                LOGGER.info("Received from NPort: " + response);
                // 根据需要解析数据
            }
        } catch (SocketException e) {
            LOGGER.warning("Connection reset by peer, attempting to reconnect.");
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, "I/O error occurred", e);
        }
    }

    private static void loadConfig() {
        try (InputStream input = NPortClient.class.getClassLoader().getResourceAsStream("config.properties")) {
            Properties prop = new Properties();
            if (input == null) {
                LOGGER.severe("Sorry, unable to find config.properties");
                return;
            }
            prop.load(input);
            serverIp = prop.getProperty("server.ip");
            serverPort = Integer.parseInt(prop.getProperty("server.port"));
            reconnectDelay = Integer.parseInt(prop.getProperty("reconnect.delay"));
        } catch (IOException ex) {
            LOGGER.log(Level.SEVERE, "Error loading configuration", ex);
        }
    }
}

欢迎大家在评论区讨论,今天的干货分享就到此结束了,如果觉得对您有帮助,麻烦给个三连!

以上内容为本人的经验总结和平时操作的笔记。若有错误和重复请联系作者删除!!感谢支持!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/714466.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习】机器学习与教育科技在个性化教学中的融合应用与性能优化新探索

文章目录 引言机器学习与教育科技的基本概念机器学习概述监督学习无监督学习强化学习 教育科技概述学生学习行为分析个性化学习路径推荐智能化教育评估 机器学习与教育科技的融合应用实时学习数据分析数据预处理特征工程 学生成绩预测与优化模型训练模型评估 个性化学习路径推荐…

盛世古董乱世金-数据库稳定到底好不好?

是不是觉得这个还用问&#xff1f; 是的要问。因为这个还是一个有争议的问题。但是争议双方都没有错。这就像辩论&#xff0c;有正反双方。大家都说的有道理&#xff0c;但是很难说谁对谁错。 正方观点&#xff1a;数据库稳定好 其实这个是用户的观点&#xff0c;应用开发人…

Docker:在DockerHub上创建私有仓库

文章目录 Busybox创建仓库推送镜像到仓库 本篇开始要学习在DockerHub上搭建一个自己的私有仓库 Busybox Busybox是一个集成了三百多个最常用Linux命令和工具的软件&#xff0c;BusyBox包含了很多工具&#xff0c;这里拉取该镜像推送到仓库中&#xff1a; 安装 apt install …

USB转I2C转SPI芯片CH341与CH347比较

1. 芯片中文资料&#xff1a; USB转I2C转SPI芯片CH341 高速USB转接芯片CH347转9M双串口转I2C转SPI转JTAG转SWD USB2.0高速转接芯片CH347应用开发手册 2. CH341与CH347比较&#xff1a; 类别CH341CH347备注串口速度2M9MCH347的串口速度更快设置CH341的I2C或SPI不能与串口同…

Matlab电话按键拨号器设计

前言 这篇文章是目前最详细的 Matlab 电话按键拨号器设计开源教程。如果您在做课程设计或实验时需要参考本文章&#xff0c;请注意避免与他人重复&#xff0c;小心撞车。博主做这个也是因为实验所需&#xff0c;我在这方面只是初学者&#xff0c;但实际上&#xff0c;从完全不…

【chatbot-api开源项目】开发文档

chatbot-api 1. 需求分析1-1. 需求分析1-2. 系统流程图 2. 技术选型3. 项目开发3-1. 项目初始化3-2. 爬取接口获取问题接口回答问题接口创建对应对象 3-3. 调用AI3-4. 定时自动化回答 4. Docker部署5. 扩展5-1. 如果cookie失效了怎么处理5-2. 如何更好的对接多个回答系统 Gitee…

Web渗透信息收集进阶

网站敏感目录与文件 网站敏感目录表示网站目录中容易被恶意人员利用的一些目录。通常恶意人员都是通过工具扫描&#xff0c;来扫出网站的敏感目录&#xff0c;敏感目录是能够得到其他网页的信息&#xff0c;从而找到后台管理页面&#xff0c;尝试进入后台等&#xff0c;扫描网…

在Ubuntu中创建Ruby on Rails项目并搭建数据库

新建Rails项目 先安装bundle Ruby gem依赖项工具&#xff1a; sudo apt install bundle 安装Node.js: sudo apt install nodejs 安装npm 包管理器&#xff1a; sudo apt install npm 安装yarn JavaScript包管理工具&#xff1a; sudo apt install yarn 安装webpacker: …

leetcode236. 二叉树的最近公共祖先

一、题目描述&#xff1a; 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 二、输入输出实例&#xff1a; 示例 1&#xff1a; 输入&#xff1a;root [3,5,1,6,2,0,8,null,null,7,4], p 5, q 1 输出&#xff1a;3 解释&#xff1a;节点 5 和节点 1 的最近公共祖先…

Ps:脚本事件管理器

Ps菜单&#xff1a;文件/脚本/脚本事件管理器 Scripts/Script Events Manager 脚本事件管理器 Script Events Manager允许用户将特定的事件&#xff08;如打开、存储或导出文件&#xff09;与 JavaScript 脚本或 Photoshop 动作关联起来&#xff0c;以便在这些事件发生时自动触…

按键输入消抖

按键输入是人机对话不可缺少的一部分&#xff0c;对于消抖设计&#xff0c;一种是软件消抖&#xff0c;一种是硬件消抖。但在单片机电路设计中&#xff0c;采用电容消抖才是最佳的选择&#xff0c;其次才是定时器消抖。 1、按键输入采用软件消抖 1)、通过定时器方式定时读取按…

【Android面试八股文】请你描述一下JVM的内存模型

文章目录 JVM内存模型1. 方法区(Method Area)运行时常量池(Runtime Constant Pool)2. 堆(Heap)3. 栈(Stack)4. 本地方法栈(Native Method Stack)5. 程序计数器(Program Counter Register)6. 直接内存(Direct Memory)JVM内存溢出的情况Java的口号是: “Write onc…

生产者消费者模型的同步与互斥:C++代码实现

文章目录 一、引言二、生产者消费者模型概述1、基本概念和核心思想2、生产者消费者模型的优点 三、消费者和生产者之间的同步与互斥四、代码实现1、事前准备2、环形队列的实现3、阻塞队列的实现4、两种实现方式的区别 一、引言 在现代计算机系统中&#xff0c;很多任务需要同时…

稀疏矩阵是什么 如何求

稀疏矩阵是一种特殊类型的矩阵&#xff0c;其中大多数元素都是零。由于稀疏矩阵中非零元素的数量远少于零元素&#xff0c;因此可以使用特定的数据结构和算法来高效地存储和处理它们&#xff0c;从而节省存储空间和计算时间。 RowPtr 数组中的每个元素表示对应行的第一个非零元…

FreeRTOS队列(queue)

队列(queue)可以用于"任务到任务"、 "任务到中断"、 "中断到任务"直接传输信息。 1、队列的特性 1、1常规操作 队列的简化操如下图所示&#xff0c;从此图可知&#xff1a; 队列中可以包含若干数据&#xff1a;队列中有若干项&#xff0c;这…

PostgreSql中使用to_char函数、date()函数可能会导致索引无法充分利用,导致查询速度无法提升

今天在处理接口请求速度慢的问题&#xff0c;惊奇的发现加了索引&#xff0c;但还是请求很忙。由于card_stop_info表有300w条数据&#xff0c;这时候关联查询非常慢&#xff0c;于是我加上匹配项索引&#xff0c;但是发现依然没有改变速度。。这时候去搜了一下才知道pgsql的to_…

javaweb 期末复习

1. JDBC数据库连接的实现逻辑与步骤以及JDBC连接配置&#xff08;单列模式&#xff09; public class JDBCUtil {// 这些换成自己的数据库 private static final String DB_URL "jdbc:mysql://localhost:3306/你的数据库名称";private static final String USER &q…

辛弃疾,笔墨剑影的一生

辛弃疾&#xff0c;字幼安&#xff0c;号稼轩&#xff0c;生于南宋高宗赵构绍兴十年&#xff08;公元1140年&#xff09;&#xff0c;卒于南宋宁宗赵扩嘉泰元年&#xff08;公元1207年&#xff09;&#xff0c;享年67岁。他是中国南宋时期著名的爱国词人&#xff0c;与苏轼并称…

Unity贪吃蛇改编【详细版】

Big and small greedy snakes 游戏概述 游戏亮点 通过对称的美感&#xff0c;设置两条贪吃蛇吧&#xff0c;其中一条加倍成长以及加倍减少&#xff0c;另一条正常成长以及减少&#xff0c;最终实现两条蛇对整个界面的霸占效果。 过程中不断记录两条蛇的得分情况&#xff0c…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 部门项目任务分配(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 部门项目任务分配(100分) 🌍 评测功能需要订阅专栏后私信联…