解码 SQL:深入探索 Antlr4 语法解析器背后的奥秘

news2024/11/26 11:26:56

探寻SQL的背后机制

前言

在数据领域,SQL(Structured Query Language)是一门广泛使用的语言,用于查询和处理数据。你可能已经使用过诸如MySQL、Hive、ClickHouse、Doris、Spark和Flink等工具来编写SQL查询。

每一种框架都提供了对应的SQL语法,可以帮助我们从庞大的数据集中提取所需的信息,但你是否思考过他们的SQL查询是如何一步一步变成底层的执行结果的呢?

这正是本文将要探讨的问题。将由浅入深了解SQL语法的背后原理,揭示SQL查询是如何读取、翻译、处理、和最终执行。

这一切都得益于一个强大的工具——语法解析器。

文章中提及的所有代码示例都可以在 GitHub 上找到:antlr4-examples

语法解析器

介绍

SQL(Structured Query Language)是数据领域中的关键工具,用于查询和操作数据库中的数据。然而,SQL查询并非像魔术一样自动执行的。在执行之前,SQL语句需要经过一个关键步骤:语法解析。

SQL语法解析是SQL查询处理的起点,它的任务是将人类可读的SQL语句转换为计算机可以理解的结构,以便进一步执行。这个过程依赖于语法解析器,它是一种软件工具,负责解释和分析SQL查询,以确保其具有正确的语法。

举个例子,想象一下,如果我们自己发明了一种特殊的SQL语言,例如我们将其命名为GlSQL,其语法规则如下:

-- 查询tableA表的前十条记录的a、b、c字段
gl a, b, c to tableA head 10;

可以想象,市面上没有其他人使用这种特殊语法,因为它是我们自己创造的。如果我们希望这种语言能够成熟且优雅地发展,我们需要解决以下两个核心问题:

  1. 词法解析:词法解析是指将文本转化为词法单元或标记,即将关键字和符号识别出来。

  2. 语法解析:语法解析是将这些词法单元按照特定规则组合成正确的语句结构。

这种自定义语法的语言被称为“领域特定语言”(DSL)。然而,要手动实现DSL的词法解析和语法解析过程相当复杂,需要字符串解析、语法树构建、节点处理等多个步骤,如下图:

在这里插入图片描述

这时,成熟的语法解析器派上了用场。它们能够自动执行这些繁琐的任务,大大简化了DSL的开发过程。这也是语法解析器的关键作用。

市面上常见的语法解析器

市场上有多个SQL语法解析器,每个都具有独特的特点和能力:

  1. ANTLR (ANother Tool for Language Recognition): ANTLR 是一种强大的语法解析器生成器,支持多种编程语言。它能够生成用于词法分析和语法解析的解析器,广泛用于生成编程语言解析器、配置文件解析器、模板引擎等。

  2. **JavaCC(Java Compiler Compiler)**是一个用于构建解析器(Parser)和词法分析器(Lexer)的工具,它专注于生成 Java 代码。JavaCC 提供了一种定义和生成解析器的方式,使你能够将自定义的语法规则转化为 Java 代码,以便解析和处理特定领域语言(DSL)或文件格式。

  3. ANTLR 4 和 JavaCC: 这两者都支持 Java 语言,并在 Java 开发领域中广泛使用。ANTLR 4 的优势之一是它支持多种语言,而 JavaCC 主要专注于 Java。选择取决于项目的需求和开发人员的偏好。

  4. Calcite: Apache Calcite 是一种灵活的开源框架,用于构建自定义 SQL 解析器和优化器。它是 Apache Flink、Apache Hive 和其他项目的一部分,用于处理 SQL 查询。Calcite 允许用户定义自己的 SQL 方言,并进行查询优化。

Antlr4

介绍

ANTLR (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, or translating structured text or binary files. It’s widely used to build languages, tools, and frameworks. From a grammar, ANTLR generates a parser that can build and walk parse trees.

ANTLR(另一个语言识别工具)是一个功能强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。它被广泛用于构建语言、工具和框架。从语法中,ANTLR生成一个可以构建和遍历解析树的解析器。

市场应用

ANTLR 4被许多知名的企业和项目广泛使用。这些企业和项目包括:

  1. Twitter: Twitter 使用ANTLR来解析和分析用户的查询语言,这有助于他们的搜索和分析功能。

  2. IBM: IBM使用ANTLR来支持一些其产品和工具中的DSL(领域特定语言)解析需求,例如,在其企业集成解决方案中。

  3. Apache Hive: Apache Hive,用于大数据分析,也使用ANTLR来解析Hive查询语言。

  4. Apache Spark: Apache Spark,流行的大数据处理框架,使用ANTLR作为其SQL解析器的一部分,支持SQL查询。

  5. Apache Solr: Apache Solr是一个开源搜索平台,它使用ANTLR来解析查询表达式以进行高级搜索。

使用方式

ANTLR 4主要用于生成解析器和分析器,可以将这些生成的代码集成到自己的项目中。下面是一些与ANTLR 4相关的使用方式:

  1. 通过pip下载ANTLR 4运行时库: 使用pip(Python的包管理工具)下载ANTLR 4的运行时库,以便在Python项目中使用ANTLR 4生成的解析器。安装ANTLR 4运行时库后可以将其导入并在Python代码中使用。

  2. 下载源码并使用命令行工具:可以下载 ANTLR4 的源码,并使用命令行工具来编译和运行它。这需要手动设置一些环境变量,并了解如何使用命令行工具来编译和运行 ANTLR4。

  3. 在IDE中使用ANTLR 4插件及三方库: ANTLR 4有官方支持的IDE插件,如ANTLRWorks和ANTLR4 Grammar Plugin for IntelliJ IDEA。可以使用这些插件来创建和编辑ANTLR 4语法文件,然后生成解析器和词法分析器的代码。这些插件通常提供可视化工具来帮助我们调试和测试语法规则。

安装插件

首先需要在IDEA中安装antlr4插件,ANTLR 4插件对于在InIDEA中使用ANTLR 4非常有用,尤其是在处理ANTLR语法文件、生成代码以及进行调试时,如下图:

在这里插入图片描述

编写语法文件

ANTLR4使用.g4语法文件作为输入,这些文件定义了一种形式化的语法规则,描述了编程语言、数据格式或通用文本输入的结构。

开发人员根据目标语言的数据格式和语法规则,编写.g4文件。这些规则定义了输入文本的结构,如词法分析器(lexer)和语法分析器(parser)的规则,如下:

// 语法文件通常以 granmar 关键子开头 这是一个名为 JsonParser 的语法 它必须和 JsonParser.g4文件名相匹配
grammar JsonParser;

// 定义一条名为 json 的语法规则,它匹配一对花括号[START, STOP为词法关键词]、逗号分隔的 value [另一条语法规则,在下面], 以及 * 匹配多个 value
json : START value (',' value)* STOP ;

// 定义一条value的语法规则,正是上面json语法中的value,该value的值应该是 INT 或者继续是 json [代表嵌套], | 符号代表或
value :
      |json
      |INT
      ;

// 以下所有词法符号都是根据正则表达式判断
// 定义一个INT的词法符号, 只能是正整数
INT : [0-9]+ ;

// 定义一个START的词法符号, 只包含{
START : '{' ;

// 定义一个STOP的词法符号, 只包含}
STOP : '}' ;

// 定义一个AND的词法符号, 只包含,
AND : ',' ;

这是一个经典的ANTLR4的语法文件示例,用于解析JSON格式的数据,此时我们运行antlr4插件简单测试一下,如下:在语法文件中右键跟节点-> Test Rule json

在这里插入图片描述

在左侧输入框中输入特定语法右侧即会展示语法树,如下:

在这里插入图片描述

至此我们从理论层面初步体验了Antlr4的语法解析过程,接下来要结合代码使用

生成Java解析类

在上一步中,我们仅使用IDEA的Antlr4插件来验证了语法文件,但光有语法文件是不够的,实际应用中我们需要将其与代码结合起来并进行实际操作,而生成代码这一步骤也可以通过Antlr4插件来实现,首先需要指定Antlr4插件生成java类的路径,如下:右键JsonParser.g4 -> Configure

在这里插入图片描述
在这里插入图片描述

  • 生成java文件

在这里插入图片描述

此时生成的java类便是Antlr4所提供的核心功能,将AST语法树转化成类的表达方式,新建一个测试类复制如下代码:

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.Test;

public class Example {

    @Test
    public void demo() {

        ANTLRInputStream input = new ANTLRInputStream("{1,2,{3,4}}");
        //词法解析器,处理input
        JsonParserLexer lexer = new JsonParserLexer(input);
        //词法符号的缓冲器,存储词法分析器生成的词法符号
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        //语法分析器,处理词法符号缓冲区的内容
        JsonParserParser parser = new JsonParserParser(tokens);

        ParseTree tree = parser.json();
        System.out.println(tree.toStringTree(parser));
    }

}

在ParseTree中包含着children集合,在集合中抱着各个节点,每个节点又可以向下展开,从而形成类形式的语法树,如下:

在这里插入图片描述

自定义处理规则

在上一步中Antlr4帮我们将{1,2,{3,4}}字符串转化成了类形式的语法树,Antlr4生成的语法树只是一种理解和解析语言结构的方式,真正的业务逻辑处理还需要在语法树的基础上进行。就拿sql举例,sql语言解析成了语法树是远远不够的,还需要让语法树落地成读取物理文件的可执行的代码。

假设我们现在的规则是需要将{}中的所有数值相加求和,最后得到总和,那么该如何自定义呢?

  • Antlr4给我们提供了两种遍历树的方式:

  • 1、监听器模式–antlr4内部控制遍历语法树规则

  • 2、访问者模式—用户可以手动控制遍历语法树规则

这两种方式在此示例中的体现是两个接口【antlr4帮我们生成】,并且还帮我们生成了默认实现类:

在这里插入图片描述

监听器模式

监听器模式的特点是用户无需关心语法树的递归,统一由antlr提供的ParseTreeWalker类进行递归即可。

我们先自行实现ParseTreeListener接口,在其中填充自己的逻辑代码(通常是调用程序的其他部分),从而构建出我们自己的语言类应用程序,如下:

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.HashMap;
import java.util.Map;

public class JsonParserListenerExample implements JsonParserListener {

    Map<String, Integer> map = new HashMap<>();

    @Override
    public void enterJson(JsonParserParser.JsonContext ctx) {
        if (!map.containsKey(ctx.getText())) {
            map.put(ctx.getText(), 0);
        }
    }

    @Override
    public void exitJson(JsonParserParser.JsonContext ctx) {
        if (ctx.parent == null) {
            int sum = map.values().stream().mapToInt(i -> i).sum();
            System.out.println(" result = " + sum);
        }
    }

    @Override
    public void enterValue(JsonParserParser.ValueContext ctx) {
        if (ctx.INT() != null && map.containsKey(ctx.parent.getText())) {
            map.put(ctx.parent.getText(), map.get(ctx.parent.getText()) + Integer.parseInt(ctx.INT().getText()));
        }
    }

    @Override
    public void exitValue(JsonParserParser.ValueContext ctx) {

    }
}
  • 测试:
@Test
public void demoListener(){

    ANTLRInputStream input = new ANTLRInputStream("{1,2,{3,4},{3,4}}");
    //词法解析器,处理input
    JsonParserLexer lexer = new JsonParserLexer(input);
    //词法符号的缓冲器,存储词法分析器生成的词法符号
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    //语法分析器,处理词法符号缓冲区的内容
    JsonParserParser parser = new JsonParserParser(tokens);

    ParseTree tree = parser.json();
    // ParseTreeWalker类将实现的MeSqlParserBaseListener监听器放入
    new ParseTreeWalker().walk(new JsonParserListenerExample(), tree);
}

这里说一下执行流程:

在JsonParserListenerExample类中,语法中的每条规则都有对应的enter方法和exit方法。

例如,当遍历器访问到json规则对应的节点时,它就会调用enterJson()方法,然后将对应的AST语法树节点 JsonContext的实例当作参数传递进去,在遍历器访问了Json节点的全部子节点之后,它会调用exitJson()函数;

如果执行到叶子节点,它会调用enterValue()方法,将对应的语法树节点 ValueContext的实例当作参数传递给它,执行完成后执行exitValue()方法。

下图用标识了 ParseTreeWalker对AST语法树进行深度优先遍历的过程:

在这里插入图片描述

至此监听器程序结束。

访问者模式

访问者模式是23种设计模式中最复杂的模式,可参考:23-design-pattern

访问者模式的特点是需要用户自己手动控制语法树节点的调用,优点是灵活,sparksql也是使用这一模式来实现sql语法解析

在JsonParserVisitorExample中,语法里的每条规则对应接口中的一个visit方法

import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.List;

public class JsonParserVisitorExample implements JsonParserVisitor<Integer> {

	@Override
	public Integer visitJson(JsonParserParser.JsonContext ctx) {
		List<JsonParserParser.ValueContext> value = ctx.value();
		return value.stream().mapToInt(this::visitValue).sum();
	}

	@Override
	public Integer visitValue(JsonParserParser.ValueContext ctx) {
		if (ctx.json() != null) {
			return visitJson(ctx.json());
		}
		if (ctx.INT() != null) {
			return Integer.parseInt(ctx.INT().getText());
		}
		return 0;
	}

	@Override
	public Integer visit(ParseTree parseTree) {
		return null;
	}

	@Override
	public Integer visitChildren(RuleNode ruleNode) {
		return null;
	}

	@Override
	public Integer visitTerminal(TerminalNode terminalNode) {
		return null;
	}

	@Override
	public Integer visitErrorNode(ErrorNode errorNode) {
		return null;
	}
}
  • 测试:
@Test
public void demoVisitor() {

    ANTLRInputStream input = new ANTLRInputStream("{1,2,{3,4},{3,4}}");
    //词法解析器,处理input
    JsonParserLexer lexer = new JsonParserLexer(input);
    //词法符号的缓冲器,存储词法分析器生成的词法符号
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    //语法分析器,处理词法符号缓冲区的内容
    JsonParserParser parser = new JsonParserParser(tokens);

    JsonParserVisitorExample jsonParserVisitorExample = new JsonParserVisitorExample();
    Integer sum = jsonParserVisitorExample.visitJson(parser.json());
    System.out.println(sum);
}

至此访问者模式结束。

使用总结

至此我们用两种方式实现了一个简单的DSL语言,回过头来再看一下开篇定义:

ANTLR是一款强大的语法分析器生成工具,可用于读取、处理、执行和翻译结构化的文本,用户可根据需要自定义语法规则来实现相应功能。

SparkSql中的应用

语法

  • 这里以sparkSql:3.0为例,语法文件地址:SqlBase.g4

  • 接下来我们将这该文件复制到IDEA中,打开SqlBaseParser.g4,右键执行Test Rule

在这里插入图片描述

  • 随便输入一条sql,查看右侧语法树:可以看到右侧生成了庞大的语法树,这就是SparkSQL的语法树

在这里插入图片描述

  • 接下来我们可以根据语法文件来生成相关配置类:

在这里插入图片描述

  • 此时我们查看工程中spark-catalyst依赖的parser包,可以看出两者完全一样

在这里插入图片描述

  • 由于sparksql是通过访问器模式实现递归调用语法树,故这里看SqlBaseBaseVisitor,发现真正实现的是子类:AstBuilder、SparkSqlAstBuilder,其内部实现函数便是sparksql各个节点的执行逻辑

在这里插入图片描述

示例

  • 接下来我们试着改一下Spark的Sql语法,新建一个类来自定义访问器
public class SqlBaseVisitorExample extends SqlBaseBaseVisitor<String> {
    @Override
    public String visitSingleStatement(SqlBaseParser.SingleStatementContext ctx) {
        System.out.println(" ...SqlBaseVisitorExample... "); // 打印
        return visitChildren(ctx);
    }
}
  • 测试类
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.junit.Test;

public class Example {

    @Test
    public void demoVisitor() {
        String query = "SELECT * FROM STUDENT WHERE ID > 10;";
        SqlBaseLexer lexer = new SqlBaseLexer(new ANTLRInputStream(query.toUpperCase()));
        SqlBaseParser parser = new SqlBaseParser(new CommonTokenStream(lexer));

        // 创建自定义访问器
        SqlBaseVisitorExample visitor = new SqlBaseVisitorExample();
        // 将parser语法树头节点放入
        visitor.visitSingleStatement(parser.singleStatement());
    }

}

至此SparkSql中涉及antlr4语法解析器阶段结束

相关文档

  • ANTLR4官网

  • ANTLR4-GitHub

  • SPARK官网

  • SPARK-GitHub

  • 23种设计模式

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ASEM工控机维修工业电脑控制器维修PB3400

ASEM工控机维修asem工业电脑维修常见型号&#xff1a;PB3400;PB2000;PB3200;PB3600&#xff1b;BM2200等。 ASEM工控机维修常见故障有&#xff1a;开不了机、黑屏、不能启动、电路板故障、主板、开机没反应、显示器没反应、主板故障、蓝屏、卡机、显示器信号灯一直闪、系统不能…

PlantUML语法(全)及使用教程-用例图

目录 1. 用例图1.1、什么是用例图1.2、用例图的构成1.3、参与者1.4、用例1.4.1、用例基本概念1.4.2、用例的识别1.4.3、用例的要点1.4.3、用例的命名1.4.4、用例的粒度 1.5、应用示例1.5.1、用例1.5.2、角色1.5.3、改变角色的样式1.5.4、用例描述1.5.5、改变箭头方向1.5.6、使用…

“通识+产业”大模型,“Alaya元识”的赋能路径

2023年11月&#xff0c;国家工业信息安全发展研究中心、工信部电子知识产权中心发布的《中国AI大模型创新和专利技术分析报告》显示&#xff0c;我国大模型专利申请总数已突破4万余件&#xff0c;大模型相关领域的创新日益活跃。 相对于“能做诗会画画”的针对to C市场的大模型…

东南大学与OpenHarmony携手共建开源生态,技术俱乐部揭牌成立并迎来TSC专家进校园

11月25日,OpenAtom OpenHarmony(以下简称“OpenHarmony”)项目群技术指导委员会(以下简称“TSC”)与东南大学携手,于东南大学九龙湖校区金智楼一楼报告厅举办了“东南大学OpenHarmony技术俱乐部成立仪式暨OpenHarmony TSC专家进校园”活动。此次盛会标志着OpenHarmony开源社区和…

TCP 基本认识

1&#xff1a;TCP 头格式有哪些&#xff1f; 序列号&#xff1a;用来解决网络包乱序问题。 确认应答号&#xff1a;用来解决丢包的问题。 2&#xff1a;为什么需要 TCP 协议&#xff1f; TCP 工作在哪一层&#xff1f; IP 层是「不可靠」的&#xff0c;它不保证网络包的交付…

Alivia 1.0 正式版来了,打造更懂企业的营销「工具箱」

上周&#xff0c;「Whale 帷幄」2023 秋季发布会圆满落下帷幕。发布会上&#xff0c;帷幄创始人 & CEO 叶生晅重磅发布了专为营销和销售设计的企业级 AGI 工具——Alivia 1.0 正式版&#xff0c;获得了广泛的反响和好评。 在这一年里&#xff0c;帷幄在 AGI 产品创新及落地…

力扣刷题篇之分治

系列文章目录 目录 系列文章目录 前言 一、分解问题 二、解决子问题 三、合并结果 总结 前言 刷题按照&#xff1a; [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 - 力扣&#xff08;LeetCode&#xff09; 参考&#xff1a; 「五大常用算法」一文搞懂分治算法…

欧拉LINUX 23.09版本上安装ORACLE 19c

前面解决了在RHEL9上安装ORACLE 19C的问题后&#xff0c;发现龙蜥 LINUX23 上可以安装ORACLE19C,网上搜了一下&#xff0c;欧拉 linux 22.03 上&#xff0c;没有成功安装ORACLE 19c 的先例&#xff0c;23.09就更不用说了&#xff0c;但看到的错误&#xff0c;不外服都是缺 libp…

泛微OA对接金蝶云星空方案分享(对接场景解析)

分享金蝶云星空跟泛微OA系统集成对接的方案分享&#xff0c;主讲审批流程对接&#xff0c;表单对接的两类场景。分别是金蝶云星空发起申请和泛微发起流程审批&#xff0c;最终实现统一管理。 数据集成主要有以下好处&#xff1a; &#xff08;1&#xff09;数据一致性&#xf…

直播团队职责

一、直播策划 1.根据公司战略&#xff0c;制定直播计划和方案&#xff0c;包括直播频率、时间、主题等。 2.负责直播内容策划&#xff0c;包括商品选择、优惠策略、互动环节等。 3.分析市场竞争情况&#xff0c;调整和优化直播方案。 4.与团队协作&#xff0c;确保直播计划…

GANVAEDiffusion

数学基础 KL散度 描绘一个分布p和另一个分布q之间的偏离程度 当 p ( x ) q ( x ) p(x)q(x) p(x)q(x)时散度取得最小值 JS散度 另一种衡量两个概率分布相似性的方法 GAN 需要训练两个网络&#xff1b;损失来回波动&#xff0c;不好分辨&#xff0c;不容易收敛&#xff…

day32_Git

今日内容 零、 复习昨日 零、 复习昨日 一、引言 在单人开发过程中&#xff0c;需要进行版本管理&#xff0c;以利于开发进度的控制。 在多人开发过程中&#xff0c;不仅需要版本管理&#xff0c;还需要进行多人协同控制。 版本控制(VS) SVN GIT 二、介绍 Git是一个开源的…

WMS系统

什么是WMS系统&#xff1f; WMS&#xff08;Warehouse Management System&#xff0c;仓库管理系统&#xff09;是一种软件解决方案&#xff0c;旨在帮助用户优化仓库管理流程、管理和控制日常仓库运营。 WMS系统的主要功能有那些&#xff1f; 主要功能主要包括以下几点&…

AI伪原创软件-AI伪原创工具下载

在当今数字化时代&#xff0c;创作者们在追求独特创意的同时&#xff0c;也面临着时间和灵感的双重挑战。AI伪原创技术应运而生&#xff0c;为创作者提供了一种快捷而便利的解决方案。本文将专心分享两款备受瞩目的AI伪原创工具&#xff0c;147SEO伪原创、百度文心一言伪原创&a…

网站域名那些事儿

互联网用户对于在线数据安全的意识逐渐增强&#xff0c;因此拥有一个可靠的网络安全系统是至关重要的。而其中一个最重要的元素就是网站域名SSL证书。 SSL&#xff08;Secure Socket Layer&#xff09;是一种用于确保网站与访客之间通信安全的技术。通过使用SSL证书&#xff0c…

echarts案例网站

一、ppchart 网站&#xff1a;https://ppchart.com/#/ 二、echarts官网示例 网站&#xff1a;https://echarts.apache.org/examples/zh/index.html

[VNCTF 2023] web刷题记录

文章目录 象棋王子电子木鱼BabyGo 象棋王子 考点&#xff1a;前端js代码审计 直接查看js源码&#xff0c;搜一下alert 丢到控制台即可 电子木鱼 考点&#xff1a;整数溢出 main.rs我们分段分析 首先这段代码是一个基于Rust的web应用程序中的路由处理函数。它使用了Rust的异步…

龙迅LT8668SXC适用于TPYE-C/DP/HDMI转EDP/VBO同时环出一路HDMI/DP,支持分辨率缩放功能。

1.描述 应用功能&#xff1a;LT8668SXC适用于TYPE-C/DP1.4/HDMI2.1转EDP/VBO同时环出一路HDMI/DP应用方案 分辨率&#xff1a;高达8K30HZ&#xff0c; 工作温度范围&#xff1a;−40C to 85C 产品封装&#xff1a;QFN88 (10*10)最小包装数&#xff1a;1680pcs 2.产品应用 •视频…

BLIoTLink软网关,一键解决OT层与IT层的通信

在工业自动化领域&#xff0c;协议转换一直是一个重要的问题。不同的设备、系统往往使用不同的通信协议&#xff0c;这给数据采集、设备接入等带来很大的困扰。为了解决这个问题&#xff0c;各种协议转换软件应运而生。其中&#xff0c;BLIoTLink作为一款功能强大的嵌入式工业协…

设单链表中有仅三类字符的数据元素(大写字母、数字和其它字符),要求利用原单链表中结点空间设计出三个单链表的算法,使每个单链表只包含同类字符。

使用C语言编写的算法,将原单链表根据字符类型拆分为三个单链表。其中,大写字母链表(upperList)、数字链表(digitList)和其他字符链表(otherList)分别用于存储相应类型的字符。 `Upper Case List`存储了大写字母A、C, `Digit List`存储了数字1、2、3, `Other List`存…