展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > 在.NET中使用ANTLR4构建语法分析器的方法

在.NET中使用ANTLR4构建语法分析器的方法

前言 在 .NET 生态中,ANTLR4 是一个相当好用的语法分析器生成工具,能帮你快速搭建编译器、解释器这类东西。这篇内容会从环境搭建、语法规则编写、生成分析器,到实际调用,完整走一遍基本流程。受篇幅限制,ANTLR4 的语法规则不会深入展开,更详细的说明可以去看官方文档或者相关资料。 例子都比较

本文目录

    前言

    在 .NET 生态中,ANTLR4 是一个相当好用的语法分析器生成工具,能帮你快速搭建编译器、解释器这类东西。这篇内容会从环境搭建、语法规则编写、生成分析器,到实际调用,完整走一遍基本流程。受篇幅限制,ANTLR4 的语法规则不会深入展开,更详细的说明可以去看官方文档或者相关资料。

    例子都比较简单,也没有做严格测试,目的只是演示操作路径。实际开发中,有个不错的习惯——先去官方的 grammars-v4 仓库看看是不是已经有现成的 grammar 文件可以用:https://github.com/antlr/grammars-v4

    代码示例已经传到了 GitHub:https://github.com/eventhorizon-cli/Antlr4Demo

    ANTLR4 简介

    ANTLR(全称 Another Tool for Language Recognition)是一个老牌的语法分析器生成器,属于编译技术里的前端工具。用它写编译器、解释器、翻译器都很顺手。

    当前版本 ANTLR4 已经支持生成 Ja va、C#、Python、Ja vaScript 等多种语言的语法分析器。它的语法规则定义方式有点类似正则表达式,描述复杂语法结构时非常灵活。

    它的工作流程大致分四步:

    • 编写语法规则:通常用 .g4 文件来定义
    • 生成语法分析器:用 ANTLR4 工具输出目标语言的代码
    • 使用分析器进行语法分析:调用生成的代码,得到抽象语法树(AST)
    • 访问 AST:通过 Visitor 模式或 Listener 模式做后续处理,比如解释执行或编译转换

    语法分析基本概念

    语法分析可以分成两个阶段:词法分析和语法分析。

    • 词法分析:把字符序列拆成 token,比如把 1 + 2 拆成 1、+、2 三个 token
    • 语法分析:根据语法规则把 token 组织成树形结构——抽象语法树(AST),树上的每个节点代表一个语法单元,比如表达式、语句等

    举个例子,表达式 1 + 2 * 3 的 AST 长这样:

      +
     / \
    1   *
       / \
      2   3

    如何使用 ANTLR4

    1. 安装 Antlr4.Runtime.Standard 包

    下面用加减乘除四则运算来演示完整流程。先新建一个 C# 项目,然后添加包:

    dotnet add package Antlr4.Runtime.Standard

    2. 编写 ANTLR4 的语法规则文件

    新建一个 .g4 文件,比如 Arithmetic.g4,内容如下:

    grammar Arithmetic; // grammar name 需要和文件名一致
    
    // 语法规则
    // op=('*'|'/') 表示 op 将 ‘*' 或者 ‘/' 标记为一个操作符号
    // # MulDiv 将这个规则命名为 MulDiv,访问 AST 时会用到
    expr:
       expr op=('*'|'/') expr   # MulDiv
        |   expr op=('+'|'-') expr   # AddSub
        |   INT                      # Int
        |   '(' expr ')'             # Parens
        ;
    
    // 词法规则
    INT :   [0-9]+ ;
    WS  :   [ \t\r\n]+ -> skip ; // 表示忽略空格

    在.NET中使用ANTLR4构建语法分析器的方法

    .g4 文件由两部分组成:词法规则(通常大写开头)和语法规则(通常小写开头)。词法规则定义 token,比如数字、运算符;语法规则定义语法结构,比如表达式、语句。

    上面的例子定义了一个简单的四则运算语法,支持加减乘除和括号。整数 token 用 INT 表示(一个或多个数字)。expr 规则中用 | 分隔可选分支,乘除法写在加减法前面,这样乘除法优先级更高。每个分支可以用 # 命名,比如 # MulDiv,在 Visitor 访问时会用到这个名字。规则支持递归,比如乘法规则里又引用了 expr。

    这个例子比较简单,所以词法和语法写在一个文件里。复杂项目可以把它们分开。

    安装 ANTLR4 的插件(Rider 或 VS Code)后,可以检查语法规则是否正确。在 Rider 中选中 expr 规则,右键选择 Test Rule expr,输入测试表达式就能看到树形结构的结果。

    在.NET中使用ANTLR4构建语法分析器的方法

    在.NET中使用ANTLR4构建语法分析器的方法

    3. 生成语法分析器

    ANTLR4 本身基于 Ja va,所以需要安装 Ja va 运行环境才能使用它的工具。有两种方式生成分析器,推荐使用 Antlr4BuildTasks 自动生成。

    方式一:直接使用 ANTLR4 官方工具

    从官网下载 jar 包(本文写作时最新版 4.13.2):

    https://www.antlr.org/download/antlr-4.13.2-complete.jar

    下载后放到 .g4 文件所在目录,然后运行:

    ja va -jar antlr-4.13.2-complete.jar -Dlanguage=CSharp Arithmetic.g4

    -Dlanguage=CSharp 表示生成 C# 语言的代码。执行后会生成 ArithmeticLexer.cs、ArithmeticParser.cs 等文件,然后就可以在项目里使用了。

    在.NET中使用ANTLR4构建语法分析器的方法

    在.NET中使用ANTLR4构建语法分析器的方法

    方式二:借助 Antlr4BuildTasks 自动生成

    手动方式比较繁琐,推荐使用 Antlr4BuildTasks 这个 MSBuild 任务,它可以自动下载 ANTLR4 工具、生成分析器代码,并添加到项目中。它也会尝试下载 Ja va 运行环境,如果构建出错,可以手动安装全局 Ja va。

    项目文件(.csproj)配置如下:

    
        
            Exe
            net9.0
            enable
            enable
        
    
        
            
            
        
    
        
            
        
    

    表示项目里所有 .g4 文件都会参与编译。构建之后,生成的代码会放在 obj 文件夹中,每次构建都会重新生成,不需要手动添加。

    在.NET中使用ANTLR4构建语法分析器的方法

    4. 编写代码来使用语法分析器

    访问 AST 的方式有两种:Visitor 和 Listener。ANTLR4 会为每个 .g4 文件生成对应的 BaseVisitor 和 BaseListener,咱们只需要继承并重写相关方法即可。

    使用 Visitor 实现

    继续用四则运算的例子。ANTLR4 为我们生成了一个 ArithmeticBaseVisitor 泛型基类,对应 .g4 中命名的规则(MulDiv、AddSub、Int、Parens),基类中有 VisitMulDiv、VisitAddSub 等方法。我们写一个自定义 Visitor 重写它们:

    public class ArithmeticVisitor : ArithmeticBaseVisitor
    {
        // 解析乘除法
        public override int VisitMulDiv(ArithmeticParser.MulDivContext context)
        {
            int left = Visit(context.expr(0));
            int right = Visit(context.expr(1));
            return context.op.Text switch
            {
                "*" => left * right,
                "/" => left / right,
                _ => throw new NotSupportedException($"Operator {context.op.Text} is not supported.")
            };
        }
    
        // 解析加减法
        public override int VisitAddSub(ArithmeticParser.AddSubContext context)
        {
            int left = Visit(context.expr(0));
            int right = Visit(context.expr(1));
            return context.op.Text switch
            {
                "+" => left + right,
                "-" => left - right,
                _ => throw new NotSupportedException($"Operator {context.op.Text} is not supported.")
            };
        }
    
        // 去掉括号,访问括号内的表达式
        public override int VisitParens(ArithmeticParser.ParensContext context) => Visit(context.expr());
    
        // 解析整数
        public override int VisitInt(ArithmeticParser.IntContext context) => int.Parse(context.INT().GetText());
    }

    然后写一个 Evaluate 方法,把字符串表达式传进去:

    Console.WriteLine(Evaluate("1 + 2 * 3")); // 7
    Console.WriteLine(Evaluate("(1 + 2) * 3")); // 9
    
    int Evaluate(string expression)
    {
        var lexer = new ArithmeticLexer(new AntlrInputStream(expression));
        var tokens = new CommonTokenStream(lexer);
        var parser = new ArithmeticParser(tokens);
        var visitor = new ArithmeticVisitor();
        return visitor.Visit(parser.expr());
    }

    使用 Listener 实现

    Listener 模式通过 EnterXXX 和 ExitXXX 方法触发,我们可以在 ExitXXX 里把中间结果压入栈,最后弹出结果。

    public class ArithmeticListener : ArithmeticBaseListener
    {
        private readonly Stack _stack = new();
        public int Result => _stack.Pop();
    
        public override void ExitMulDiv(ArithmeticParser.MulDivContext context)
        {
            int right = _stack.Pop();
            int left = _stack.Pop();
            int result = context.op.Text switch
            {
                "*" => left * right,
                "/" => left / right,
                _ => throw new NotSupportedException($"Operator {context.op.Text} is not supported.")
            };
            _stack.Push(result);
        }
    
        public override void ExitAddSub(ArithmeticParser.AddSubContext context)
        {
            int right = _stack.Pop();
            int left = _stack.Pop();
            int result = context.op.Text switch
            {
                "+" => left + right,
                "-" => left - right,
                _ => throw new NotSupportedException($"Operator {context.op.Text} is not supported.")
            };
            _stack.Push(result);
        }
    
        public override void ExitInt(ArithmeticParser.IntContext context)
        {
            int value = int.Parse(context.INT().GetText());
            _stack.Push(value);
        }
    }

    使用方式类似:

    Console.WriteLine(Evaluate("1 + 2 * 3")); // 7
    Console.WriteLine(Evaluate("(1 + 2) * 3")); // 9
    
    int Evaluate(string expression)
    {
        var lexer = new ArithmeticLexer(new AntlrInputStream(expression));
        var tokens = new CommonTokenStream(lexer);
        var parser = new ArithmeticParser(tokens);
        var listener = new ArithmeticListener();
        parser.AddParseListener(listener);
        parser.expr();
        return listener.Result;
    }

    构建自定义 AST 以解决复杂问题

    上面的例子直接在遍历时计算了值,对于简单场景够用了。但如果处理逻辑比较复杂,更好的做法是把 ANTLR4 生成的原始 AST 转换成一个自定义的 AST 结构,把解析逻辑和执行逻辑拆开,代码会更清晰,扩展起来也更方便。

    下面看一个更接近实际的需求:指定一个文件夹,用 SQL 语句查询文件夹下的 CSV 文件,支持过滤、排序等。为了简化,只支持 SELECT、FROM、WHERE、ORDER BY,数据类型全当字符串处理,过滤支持 =、!=、LIKE,条件只能用 AND 连接,排序支持 ASC 和 DESC。

    这次把词法和语法规则分开成两个文件:SqlLexer.g4 和 SqlParser.g4。

    SqlLexer.g4

    lexer grammar SqlLexer;
    
    options { caseInsensitive = true; }
    
    SELECT : 'SELECT' ;
    FROM   : 'FROM' ;
    WHERE  : 'WHERE' ;
    ORDER  : 'ORDER' ;
    BY     : 'BY'  ;
    ASC    : 'ASC' ;
    DESC   : 'DESC' ;
    AND    : 'AND' ;
    OR     : 'OR' ;
    COMMA  : ',' ;
    STAR   : '*'  ;
    
    EQ     : '=' ;
    NEQ    : '!=' ;
    LIKE   : 'LIKE' ;
    
    STRING_LITERAL : '\'' ( ~('\'' | '\\') | '\\' . )* '\'' ;
    IDENTIFIER     : [a-z_][a-z0-9_]* ;
    WS             : [ \t\r\n]+ -> skip ;

    SqlParser.g4

    parser grammar SqlParser;
    
    options { tokenVocab=SqlLexer; }
    
    query : SELECT selectList FROM tableName (WHERE whereClause)? (ORDER BY orderByClause)? ;
    
    selectList : columnName (COMMA columnName)* | STAR ;
    
    columnName: IDENTIFIER ;
    tableName: IDENTIFIER ;
    
    whereClause : whereCondition (AND whereCondition)* ;
    
    whereCondition
        : columnName op=(EQ | NEQ) STRING_LITERAL
        | columnName op=LIKE STRING_LITERAL
        ;
    
    orderByClause : orderByCondition (COMMA orderByCondition)* ;
    
    orderByCondition : columnName (ASC | DESC)? ;

    写完后可以在 Rider 里用插件测试 query 规则,验证语法正确性。

    在.NET中使用ANTLR4构建语法分析器的方法

    接下来定义一组类型来表示 SQL 的自定义 AST:

    public abstract class Expression;
    
    public class QueryExpression : Expression
    {
        public required string TableName { get; init; }
        public required bool SelectAll { get; init; }
        public required IEnumerable SelectList { get; init; }
        public required IEnumerable WhereConditions { get; init; }
        public required IEnumerable OrderByConditions { get; init; }
    }
    
    public class WhereCondition : Expression
    {
        public required string ColumnName { get; init; }
        public WhereConditionOperator Operator { get; init; }
        public required string Value { get; init; }
    }
    
    public enum WhereConditionOperator
    {
        Equal, NotEqual, StartsWith, EndsWith, Contains
    }
    
    public class OrderByCondition : Expression
    {
        public required string ColumnName { get; init; }
        public bool IsDescending { get; init; }
    }

    写一个 SqlAstBuilder 类(继承 SqlParserBaseVisitor)把原始 AST 转换成自定义结构:

    public class SqlAstBuilder : SqlParserBaseVisitor
    {
        public override QueryExpression VisitQuery(SqlParser.QueryContext context)
        {
            var selectList = context.selectList();
            bool selectAll = selectList?.STAR() != null;
            var columns = selectList?.columnName()
                .Select(c => c.GetText())
                .ToList() ?? [];
    
            var tableName = context.tableName().GetText();
    
            var whereConditions = context.whereClause()
                ?.whereCondition()
                .Select(c =>
                {
                    var stringValue = c.STRING_LITERAL().GetText().Trim('\'');
                    var opText = c.op.Text.ToUpperInvariant();
                    var op = WhereConditionOperator.Equal;
                    if (opText == "=") op = WhereConditionOperator.Equal;
                    else if (opText == "!=") op = WhereConditionOperator.NotEqual;
                    else if (opText == "LIKE")
                    {
                        if (stringValue.StartsWith("%") && stringValue.EndsWith("%"))
                        {
                            op = WhereConditionOperator.Contains;
                            stringValue = stringValue.Substring(1, stringValue.Length - 2);
                        }
                        else if (stringValue.StartsWith("%"))
                        {
                            op = WhereConditionOperator.EndsWith;
                            stringValue = stringValue.Substring(1);
                        }
                        else if (stringValue.EndsWith("%"))
                        {
                            op = WhereConditionOperator.StartsWith;
                            stringValue = stringValue.Substring(0, stringValue.Length - 1);
                        }
                    }
                    else throw new NotSupportedException($"Operator {c.op.Text} is not supported.");
    
                    return new WhereCondition
                    {
                        ColumnName = c.columnName().GetText(),
                        Operator = op,
                        Value = stringValue
                    };
                })
                .ToList() ?? [];
    
            var orderByConditions = context.orderByClause()
                ?.orderByCondition()
                .Select(c => new OrderByCondition
                {
                    ColumnName = c.columnName().GetText(),
                    IsDescending = c.DESC() != null
                })
                .ToList() ?? [];
    
            return new QueryExpression
            {
                SelectAll = selectAll,
                SelectList = columns,
                TableName = tableName,
                WhereConditions = whereConditions,
                OrderByConditions = orderByConditions
            };
        }
    }

    最后实现 SqlToCsvEngine 类,负责解析 SQL、读取 CSV、过滤排序并返回结果:

    public class SqlToCsvEngine(DirectoryInfo csvDirectory)
    {
        public IEnumerable> ExecuteQuery(string query)
        {
            var lexer = new SqlLexer(new AntlrInputStream(query));
            var tokens = new CommonTokenStream(lexer);
            var parser = new SqlParser(tokens);
            var astBuilder = new SqlAstBuilder();
            var expression = astBuilder.Visit(parser.query());
    
            var csvData = ReadCsv(expression.TableName);
    
            // 过滤
            var filteredData = csvData.Where(row =>
            {
                bool isMatch = true;
                foreach (var condition in expression.WhereConditions)
                {
                    if (row.TryGetValue(condition.ColumnName, out var value))
                    {
                        isMatch = condition.Operator switch
                        {
                            WhereConditionOperator.Equal => value == condition.Value,
                            WhereConditionOperator.NotEqual => value != condition.Value,
                            WhereConditionOperator.StartsWith => value.StartsWith(condition.Value),
                            WhereConditionOperator.EndsWith => value.EndsWith(condition.Value),
                            WhereConditionOperator.Contains => value.Contains(condition.Value),
                            _ => throw new ArgumentOutOfRangeException()
                        };
                    }
                    else throw new InvalidOperationException($"Column {condition.ColumnName} does not exist in CSV file.");
                }
                return isMatch;
            });
    
            // 排序
            foreach (var orderByCondition in expression.OrderByConditions)
            {
                Func>, Func, string>,
                    IEnumerable>> orderByFunc;
                if (filteredData is IOrderedEnumerable> orderedData)
                {
                    orderByFunc = orderByCondition.IsDescending
                        ? (_, keySelector) => orderedData.ThenByDescending(keySelector)
                        : (_, keySelector) => orderedData.ThenBy(keySelector);
                }
                else
                {
                    orderByFunc = orderByCondition.IsDescending
                        ? Enumerable.OrderByDescending
                        : Enumerable.OrderBy;
                }
                filteredData = orderByFunc(filteredData, row =>
                {
                    if (row.TryGetValue(orderByCondition.ColumnName, out var value)) return value;
                    throw new InvalidOperationException($"Order by column {orderByCondition.ColumnName} does not exist in CSV file.");
                });
            }
    
            // 投影
            if (expression.SelectAll) return filteredData;
    
            var selectedData = filteredData.Select(row =>
            {
                var selectedRow = new Dictionary();
                foreach (var columnName in expression.SelectList)
                {
                    if (row.TryGetValue(columnName, out var value)) selectedRow[columnName] = value;
                    else throw new InvalidOperationException($"Column {columnName} does not exist in CSV file.");
                }
                return selectedRow;
            });
            return selectedData;
        }
    
        private IEnumerable> ReadCsv(string tableName)
        {
            var csvFile = new FileInfo(Path.Combine(csvDirectory.FullName, $"{tableName}.csv"));
            if (!csvFile.Exists) throw new FileNotFoundException($"CSV file {csvFile.FullName} does not exist.");
            using var reader = new StreamReader(csvFile.FullName);
            var headerLine = reader.ReadLine();
            if (headerLine == null) throw new InvalidOperationException($"CSV file {csvFile.FullName} is empty.");
            var headers = headerLine.Split(',');
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
                if (line == null) continue;
                var values = line.Split(',');
                yield return headers.Zip(values).ToDictionary(x => x.First, x => x.Second);
            }
        }
    }

    测试用的 CSV 文件内容:

    Name,City,Occupation,Company
    Alice,New York,Engineer,TechCorp
    Bob,Los Angeles,Designer,Creative Inc
    Ben,Atlanta,Writer,Publishing House
    Charlie,Chicago,Manager,Finance Group
    Da vid,Houston,Teacher,School District
    Eve,Miami,Student,University
    Frank,Seattle,Chef,Restaurant Co
    Grace,San Francisco,Doctor,HealthCare
    Hannah,Boston,Lawyer,Legal Partners
    Ian,Denver,Architect,BuildIt

    执行查询:

    var directory = new DirectoryInfo("/Users/hkh/Desktop/test");
    var engine = new SqlToCsvEngine(directory);
    var sql =  """
      SELECT Name, City, Occupation, Company
      FROM Employee
      WHERE City != 'Miami'
        AND Occupation LIKE '%er'
      ORDER BY Name ASC, Company DESC
      """;
    var result = engine.ExecuteQuery(sql);
    
    // 打印头部
    foreach (var column in result.First().Keys)
    {
        Console.Write($"{column}\t");
    }
    foreach (var row in result)
    {
        Console.WriteLine();
        foreach (var (_, value) in row)
        {
            Console.Write($"{value}\t");
        }
    }

    输出结果:

    Name City Occupation Company

    Alice New York Engineer TechCorp

    Ben Atlanta Writer Publishing House

    Bob Los Angeles Designer Creative Inc

    Charlie Chicago Manager Finance Group

    Da vid Houston Teacher School District

    Hannah Boston Lawyer Legal Partners

    参考资料

    https://github.com/antlr/grammars-v4

    https://wizardforcel.gitbooks.io/antlr4-short-course/content/basic-concept.html

    https://github.com/antlr/antlr4/blob/master/doc/csharp-target.md

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    精品专题 更多
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw macOS版
    AionClaw macOS版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy macOS版
    Loomy macOS版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirBuddy 3 macOS版
    AirBuddy 3 macOS版

    AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

    AirServer macOS版
    AirServer macOS版

    AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

    AnyDesk macOS版
    AnyDesk macOS版

    AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

    讯飞绘文 网页版
    讯飞绘文 网页版

    讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

    Maya(Autodesk Maya) Windows版
    Maya(Autodesk Maya) Windows版

    Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

    Aperty Windows版
    Aperty Windows版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw Windows版
    AionClaw Windows版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy Windows版
    Loomy Windows版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。