C#中SQLiteFTS全文搜索引擎实战指南

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:SQLite作为一个轻量级、自包含的数据库管理系统,在IT领域广泛使用。SQLiteFTS扩展了SQLite,允许开发人员高效地在数据库中实现全文搜索功能,这对处理大量文本数据的应用尤其有用。C#环境下,可以借助SQLite.NET库与SQLite数据库交互,并通过fts4或fts5引擎创建支持全文搜索的表。本指南将展示如何在C#中创建和使用SQLiteFTS,包括创建表、插入数据、执行全文搜索查询,并讨论如何优化搜索结果的相关性。同时,也会考虑实际应用中可能遇到的挑战,如分词、停用词处理等,并指出SQLiteFTS的适用范围及局限性。
技术专有名词:SQLITEFTS

1. SQLite数据库管理系统简介

SQLite是一种流行的轻量级数据库管理系统,它不需要单独的服务器进程,直接嵌入到应用程序中。这种设计使得SQLite非常适合资源有限的环境,如移动设备和桌面应用程序。

1.1 SQLite的特点

SQLite的核心特点包括:
- 轻量级 :不依赖于网络,部署简单,资源消耗小。
- 高效性 :支持标准的SQL语句,执行速度快。
- 跨平台 :支持多种操作系统,如Windows、macOS、Linux等。
- 易用性 :拥有友好的API接口,易于集成到不同的开发环境中。
- 无服务器 :不需要配置数据库服务器,简化了维护工作。

1.2 安装配置

安装SQLite的过程非常直接,用户可以从官方网站下载适合的版本,并将其解压到本地目录。接下来,配置环境变量,确保SQLite的可执行文件路径被添加到系统的PATH中,这样就可以在任何地方通过命令行进行数据库操作。

1.3 基本数据管理功能

SQLite提供了创建、查询、更新和删除(CRUD)数据库记录的标准SQL命令。通过这些基本操作,用户可以对存储在SQLite数据库中的数据进行管理。例如,使用 CREATE TABLE 创建表,使用 INSERT 插入数据,使用 SELECT 查询数据,以及使用 UPDATE DELETE 来修改或删除数据。

为了更好地理解SQLite数据库管理系统的操作,接下来的章节将介绍如何在C#环境下使用SQLite.NET库来操作SQLite数据库。

2. SQLiteFTS全文搜索引擎功能

2.1 FTS模块概述

2.1.1 FTS模块的起源和功能

SQLite的FTS模块是一种用于执行全文搜索的组件,它允许用户在数据库内直接进行全文检索操作。与传统的基于字段的搜索不同,全文搜索引擎可以索引数据库中一个或多个列的全部文本内容,提供快速且相关的搜索结果。

FTS模块起源于对更高效全文搜索需求的响应。在没有专门的全文搜索模块之前,开发者需要设计复杂的查询逻辑或依赖外部服务来实现类似功能,这既低效又不便于维护。引入FTS模块后,可以轻松地在SQLite数据库内实现复杂的全文搜索,极大地简化了开发流程。

FTS模块支持以下主要功能:
- 支持多种文本匹配模式,包括精确匹配和模糊匹配。
- 提供索引管理功能,允许开发者创建、更新、优化索引。
- 支持自动语言处理,包括分词和词干提取。
- 能够快速检索匹配的文本块,并按相关性排序返回。

2.1.2 FTS模块的版本迭代和主要特性

自推出以来,SQLite的FTS模块经历了多次迭代,每个新版本都带来了改进和新特性。FTS模块目前有FTS3、FTS4和FTS5三个主要版本。FTS3作为第一个广泛使用的版本,提供了全文搜索的基础功能;随后的FTS4在性能和灵活性方面做了进一步的提升;而最新的FTS5则进一步优化了搜索速度和资源使用率。

主要特性包括:
- 支持全文搜索的多种语言。
- 支持包含自定义分词器的更复杂的文本分析。
- 支持对索引进行压缩,减小数据库的存储占用。
- 提供了更多用于调整搜索结果相关性和性能的配置选项。

2.2 FTS索引的构建与管理

2.2.1 索引创建过程和方法

在SQLite中创建一个全文索引的基本步骤通常包括确定要索引的表和列,然后创建FTS表并对其进行配置以包含所需的数据。FTS表不同于传统表,它实际上是一个虚拟表,用于存储索引数据。

具体操作通常如下:
1. 首先选择一个或多个要进行全文搜索的列。
2. 使用特定的FTS创建语句来生成一个全文索引表。
3. 使用 INSERT 语句向FTS表中添加数据。
4. 使用 UPDATE DELETE 语句更新索引,以反映基础表的变化。

例如,创建一个包含全文搜索功能的FTS表的SQL命令可能如下所示:

CREATE VIRTUAL TABLE docIndex USING fts4(content);

上述命令创建了一个名为 docIndex 的新虚拟表,其中 content 列被全文搜索索引。这个过程会自动构建索引,并且所有数据的插入和更新将自动反映在索引中。

2.2.2 索引维护和优化策略

全文索引虽然提高了搜索效率,但同时也增加了对存储空间和计算资源的需求。因此,索引的维护和优化变得尤为重要。为了确保索引的效率和准确性,开发者需要定期进行索引的优化和更新。

索引维护的主要策略包括:
- 定期使用 REINDEX optimize 命令来压缩和优化索引文件。
- 在数据变更时,利用触发器来实时更新索引,保持索引和数据的同步。
- 定期清理无用或过时的索引项,以节省存储空间。

例如,对索引进行优化的命令如下:

optimize docIndex;

上述命令会对 docIndex 表执行优化操作,压缩索引文件,并提高后续查询的性能。

2.3 FTS搜索语法与操作

2.3.1 基本搜索语法和选项

SQLiteFTS支持多种搜索语法选项,允许用户以简单或复杂的方式进行搜索。基本的搜索语法包括对一个或多个单词的匹配,还可以使用多种操作符来扩展搜索的灵活性。

常用操作符包括:
- AND :默认的逻辑操作符,用于组合多个搜索条件,所有条件都必须满足。
- OR :用于返回满足任一条件的结果。
- NEAR :返回接近给定位置的词的结果。
- > < :分别用于返回大于和小于特定值的词的结果。

例如,要执行包含 “apple” 和 “banana” 的搜索,可以使用如下语句:

SELECT * FROM docIndex WHERE docIndex MATCH 'apple AND banana';

2.3.2 复杂查询操作和性能分析

除了基本的搜索功能,SQLiteFTS还支持复杂的查询操作,比如使用通配符、自定义分词器、排序和限制结果集等。这些操作可以让用户更精确地控制搜索结果,并提高用户体验。

复杂查询示例:

SELECT * FROM docIndex WHERE docIndex MATCH '"apple juice"~10';

上述查询将返回文档,其中”apple”和”juice”之间的文本距离不超过10个单词。

为了优化性能,建议在查询前对索引进行优化,并且限制返回结果的数量,以减少查询时间。例如,使用 LIMIT 子句限制返回结果的数量:

SELECT * FROM docIndex WHERE docIndex MATCH 'apple*' LIMIT 10;

性能分析可以通过执行计划来完成,如下所示:

EXPLAIN QUERY PLAN SELECT * FROM docIndex WHERE docIndex MATCH 'apple*';

上述命令将提供一个查询计划,帮助开发者了解查询的执行效率和潜在的性能瓶颈。

3. C#环境下的SQLite.NET库使用

在本章节中,我们将深入探讨如何在C#开发环境中利用SQLite.NET库来管理SQLite数据库。从库的安装配置到实际的数据库操作,本章节将提供一系列实践指导和代码示例。

3.1 SQLite.NET库安装与配置

3.1.1 SQLite.NET库的引入和配置过程

SQLite.NET是SQLite数据库的.NET封装库,它为.NET开发者提供了一个简单易用的接口来进行SQLite数据库的操作。在C#项目中引入SQLite.NET库,可以通过NuGet包管理器来完成,它简化了安装和维护过程。

要安装SQLite.NET库,首先打开你的C#项目,然后按照以下步骤操作:

  1. 在Visual Studio中,点击”工具” -> “NuGet包管理器” -> “管理解决方案的NuGet包…”。
  2. 在NuGet包管理器中,切换到”浏览”标签页。
  3. 在搜索框中输入”SQLite.NET”,在搜索结果中选择合适的SQLite.NET版本进行安装。

安装完成后,你可以通过 using 指令在C#文件中引入SQLite命名空间,之后就可以在项目中使用SQLite.NET提供的类和方法了。

using System;
using System.Data;
using SQLite;

class Program
{
    static void Main()
    {
        // 这里将使用SQLite.NET进行数据库操作
    }
}

3.1.2 SQLite.NET与.NET环境的兼容性分析

SQLite.NET库旨在与.NET框架兼容,可以运行在.NET Framework以及.NET Core环境中。它支持多种.NET平台,包括但不限于Windows、Linux、macOS等操作系统。

对于.NET Framework,SQLite.NET库需要.NET Framework版本4.5及以上。对于.NET Core,它支持.NET Core 2.1及以上版本。在选择版本时,请确保与你的应用程序的目标框架保持一致。

此外,SQLite.NET库具有很高的灵活性,支持多种数据库操作,如创建、读取、更新、删除(CRUD)等。它还支持事务处理,使得数据操作能够被批处理和回滚,增加了数据操作的安全性。

3.2 SQLite.NET的基本数据库操作

3.2.1 连接数据库和执行SQL语句

在SQLite.NET中,首先需要创建一个SQLite数据库连接。在C#代码中,可以使用 SQLiteConnection 类来实现这一点。以下是一个创建和打开数据库连接的示例:

string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "YourDatabaseName.db3");
using (var connection = new SQLiteConnection("Data Source=" + dbPath))
{
    connection.Open();

    // 创建表的SQL命令
    string createTableCommand = @"
        CREATE TABLE IF NOT EXISTS Users (
            Id INTEGER PRIMARY KEY AUTOINCREMENT,
            Name TEXT NOT NULL,
            Email TEXT NOT NULL UNIQUE
        )
    ";

    using (var command = new SQLiteCommand(createTableCommand, connection))
    {
        command.ExecuteNonQuery();
    }
}

这段代码首先构建了一个数据库文件的路径,然后创建并打开一个数据库连接。随后,使用 SQLiteCommand 类执行一个SQL命令来创建一个用户表。

3.2.2 数据库事务处理和异常管理

在对数据库进行操作时,如插入、更新、删除等操作,保证数据的一致性和完整性非常重要。SQLite.NET提供了事务处理机制,确保操作要么全部成功,要么全部不发生。

下面是一个使用事务的示例:

using (var transaction = connection.BeginTransaction())
{
    try
    {
        string insertUserCommand = "INSERT INTO Users (Name, Email) VALUES ('John Doe', 'john.doe@example.com')";
        using (var command = new SQLiteCommand(insertUserCommand, connection))
        {
            command.ExecuteNonQuery();
        }

        transaction.Commit();
    }
    catch (Exception ex)
    {
        transaction.Rollback();
        Console.WriteLine("Transaction rolled back. Exception: " + ex.Message);
    }
}

这段代码演示了如何使用 BeginTransaction 方法开始一个事务,执行插入命令,然后根据是否发生异常来提交或回滚事务。

3.3 SQLite.NET高级功能应用

3.3.1 复杂查询和数据映射

SQLite.NET不仅支持基本的SQL查询,还支持复杂查询。它还能将查询结果直接映射到C#对象,使得操作更加直观和方便。数据映射通常依赖于 SQLiteConnection.GetMapping<T>() 方法。

例如,有一个用户表,包含用户ID、姓名和电子邮件等字段,可以创建一个 User 类,并使用SQLite.NET将查询结果映射到这个类的实例中:

public class User
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

// ...

using (var connection = new SQLiteConnection("Data Source=" + dbPath))
{
    connection.GetMapping<User>();
    var query = connection.Table<User>();
    var users = query.ToList();
    foreach (var user in users)
    {
        Console.WriteLine($"{user.Id}: {user.Name}, {user.Email}");
    }
}

这段代码将创建一个查询,获取所有用户,并将查询结果映射到 User 类的实例中。

3.3.2 数据库同步和异步操作模式

在C#应用中,特别是在UI线程中,数据库操作可能会变得非常耗时。为了不阻塞UI线程,SQLite.NET提供了异步操作API,允许开发者以非阻塞方式执行数据库操作。

异步API使用 async await 关键字,在C# 5.0及以后版本中得到支持。以下是一个异步查询操作的示例:

public async Task<List<User>> GetUsersAsync()
{
    using (var connection = new SQLiteAsyncConnection("Data Source=" + dbPath))
    {
        return await connection.Table<User>().ToListAsync();
    }
}

这个 GetUsersAsync 方法返回一个用户列表的异步操作,可以在异步环境中调用,例如在UI框架的后台任务处理中。

本章节介绍了在C#环境下使用SQLite.NET库操作SQLite数据库的基本方法,包括库的安装配置、基本的数据库操作、以及复杂查询和数据映射,最后介绍了数据库操作的同步和异步模式。通过这些知识和技巧,开发者可以在C#项目中有效地管理SQLite数据库,从而构建出高效且功能丰富的应用程序。

4. SQLiteFTS实战应用

4.1 创建支持全文搜索的FTS表

4.1.1 FTS表的结构和特性

全文搜索表(FTS表)是SQLiteFTS用于存储数据的特殊类型的表,它能够对表中的文本数据进行快速查询。FTS表通常包含有以下特性:

  • 自动索引:创建FTS表后,无需手动创建索引,SQLiteFTS会自动创建并维护索引。
  • 基于虚拟表:FTS表是虚拟表,这意味着它们不在磁盘上存储任何数据,所有数据操作都是通过内置的全文索引机制进行。
  • 数据实时更新:对FTS表的任何数据更改都会立即反映在全文索引中,无需额外的索引更新操作。

4.1.2 在C#中创建FTS表的代码实例

在C#项目中,可以通过SQLite.NET库创建FTS表。以下是创建一个简单的FTS表的示例代码:

// 引入SQLite.NET库
using System.Data.SQLite;
using System;

namespace FtsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // 连接到SQLite数据库(如果不存在则创建)
            using (var connection = new SQLiteConnection("Data Source=test.db;Version=3;"))
            {
                connection.Open();
                // 创建FTS表
                string createTableQuery = @"
                    CREATE VIRTUAL TABLE IF NOT EXISTS fts_table USING fts4(
                        id INTEGER PRIMARY KEY,
                        title TEXT,
                        content TEXT
                    )";
                using (var command = new SQLiteCommand(createTableQuery, connection))
                {
                    command.ExecuteNonQuery();
                }
                // 创建完成后,可以通过标准的SQL插入数据
                // INSERT INTO fts_table (id, title, content) VALUES (1, 'Title', 'Content');
                Console.WriteLine("FTS table created successfully.");
            }
        }
    }
}

在上述代码中,我们首先建立了一个与SQLite数据库的连接,然后执行了创建表的SQL命令。这里 fts_table 是我们创建的FTS表的名称,它包括一个自增的 id 字段、一个 title 字段和一个 content 字段。由于使用了 fts4 模块,SQLite会自动为这些文本字段创建全文索引。

4.2 插入数据到FTS表

4.2.1 插入数据的语句和方法

向FTS表插入数据与插入到普通表中并无太大差异。然而,由于FTS表结构的特殊性,插入数据时需要确保数据类型与表定义相匹配。对于FTS表,您应该直接使用标准的 INSERT 语句来添加新的数据行。

// 插入数据到FTS表的示例代码
using (var command = new SQLiteCommand(@"INSERT INTO fts_table (id, title, content) VALUES (1, 'Example Title', 'Example content for full-text search.');", connection))
{
    command.ExecuteNonQuery();
}

4.2.2 批量数据插入的优化技巧

当需要向FTS表中插入大量数据时,逐条插入效率较低。我们可以利用SQLite的事务功能来批量插入数据,以提高数据插入效率。

using (var transaction = connection.BeginTransaction())
{
    try
    {
        string[] titles = { "Title 1", "Title 2", "Title 3" };
        string[] contents = { "Content for title 1.", "Content for title 2.", "Content for title 3." };

        foreach (var title in titles)
        {
            foreach (var content in contents)
            {
                using (var command = new SQLiteCommand(@"INSERT INTO fts_table (title, content) VALUES (@title, @content);", connection))
                {
                    command.Parameters.AddWithValue("@title", title);
                    command.Parameters.AddWithValue("@content", content);
                    command.ExecuteNonQuery();
                }
            }
        }
        transaction.Commit();
        Console.WriteLine("Batch insert completed successfully.");
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error occurred: " + ex.Message);
        transaction.Rollback();
    }
}

在此代码示例中,我们首先开始了一个事务,然后通过双层循环遍历数组中的数据进行批量插入。成功插入所有数据后,我们提交事务。如果发生异常,事务将被回滚。

4.3 使用MATCH操作符进行全文搜索

4.3.1 MATCH操作符的使用方法和示例

在SQLiteFTS中, MATCH 操作符用于全文搜索。它可以根据用户提供的文本搜索条件对FTS表中的索引进行查询,并返回匹配的行。

// 使用MATCH操作符进行全文搜索的示例代码
string searchTerm = "example";
using (var command = new SQLiteCommand(@"SELECT rowid, title, content FROM fts_table WHERE fts_table MATCH @searchTerm;", connection))
{
    command.Parameters.AddWithValue("@searchTerm", searchTerm);
    using (var reader = command.ExecuteReader())
    {
        while (reader.Read())
        {
            Console.WriteLine("Found: " + reader.GetString(1) + " - " + reader.GetString(2));
        }
    }
}

在此代码段中,我们执行了一个 SELECT 语句,使用 MATCH 操作符来搜索包含”example”一词的所有行。搜索结果将显示 rowid title content 字段。

4.3.2 搜索结果的排序和限制

当进行全文搜索时,通常需要根据相关性对结果进行排序,并可能限制返回结果的数量。可以通过 ORDER BY 子句根据相关性得分 fts_score 对结果进行排序,并使用 LIMIT 子句限制结果数量。

// 搜索结果排序和限制的示例代码
string searchTerm = "title content";
using (var command = new SQLiteCommand(@"SELECT rowid, title, content, fts_score(fts_table) AS score 
    FROM fts_table 
    WHERE fts_table MATCH @searchTerm 
    ORDER BY score DESC 
    LIMIT 10;", connection))
{
    command.Parameters.AddWithValue("@searchTerm", searchTerm);
    using (var reader = command.ExecuteReader())
    {
        while (reader.Read())
        {
            Console.WriteLine("Score: " + reader.GetDouble(3) + " - " + reader.GetString(1) + " - " + reader.GetString(2));
        }
    }
}

此代码段展示了如何对搜索结果进行排序和限制。 fts_score 函数返回每条搜索结果的相关性评分,而 ORDER BY score DESC 则根据评分降序排序结果。 LIMIT 10 限制了最多返回10条结果。

通过上述实践操作,我们详细介绍了在C#项目中创建FTS表、插入数据以及执行全文搜索的过程。接下来的章节将深入探讨如何优化全文搜索结果的相关性、处理分词、停用词、同义词以及元数据排序和过滤,并对SQLiteFTS的适用场景与局限性进行分析。

5. 全文搜索的深入优化与应用场景

全文搜索不仅仅是简单地匹配关键词,实际上,一个高效且高质量的全文搜索引擎需要经过深思熟虑的优化才能实现用户对搜索结果的相关性和准确性需求。本章将深入探讨如何优化搜索结果的相关性,处理分词、停用词、同义词问题,以及如何通过元数据进行排序和过滤,并最终分析SQLiteFTS的适用场景与局限性。

5.1 优化搜索结果的相关性

相关性是评估全文搜索系统好坏的重要指标,它直接影响到用户的满意度和系统的使用效率。

5.1.1 相关性算法和评分机制

SQLiteFTS使用了一种特殊的算法来评估查询词与文档之间的相关性,这一算法是基于包含匹配和频率统计的。当进行全文搜索时,SQLiteFTS会计算每个匹配项的“得分”,得分越高表示该文档与查询关键词的相关性越高。

-- 示例:计算并显示相关性得分
SELECT rowid, snippet(matchinfo(table), 0, '...') FROM table WHERE table MATCH 'search query';

5.1.2 实践中的相关性优化技巧

在实际应用中,提升相关性的一些有效技巧包括:

  • 调整匹配顺序 :通过 ORDER BY 语句对结果集按相关性得分进行排序,以优先显示相关性最高的结果。
  • 使用特殊语法 :利用SQLiteFTS特有的语法进行高级查询,比如使用 NEAR 操作符来优化词组匹配。
  • 自定义排名函数 :通过实现自定义的排名函数来调整相关性得分的计算方法,从而提升结果的相关性。

5.2 分词、停用词、同义词处理

分词、停用词、同义词的处理是提高搜索结果相关性的关键步骤。

5.2.1 自定义分词器的实现与配置

自定义分词器允许更精细地控制文本如何被分割为搜索词。在SQLiteFTS中,可以通过实现自定义的 tokenize 函数来构建分词器。

CREATE VIRTUAL TABLE tokenize_test USING fts3(
  tokenize=MyTokenizerClass
);

-- MyTokenizerClass是一个C#类的示例,实现自定义分词逻辑
public class MyTokenizerClass :Tokenizer
{
  public override void Tokenize(TextReader reader, TokenizerCallback callback)
  {
    // 分词逻辑实现
  }
}

5.2.2 停用词表的管理和应用

停用词表包含了一系列通常在搜索中忽略的词。通过应用停用词表,可以减少噪音,提高搜索结果的质量。

-- 创建并应用停用词表
CREATE TABLE stop_words(word TEXT PRIMARY KEY);

INSERT INTO stop_words VALUES('a');
INSERT INTO stop_words VALUES('the');
-- ... 其他停用词

CREATE VIRTUAL TABLE my_table USING fts4(stopwords='stop_words');

5.2.3 同义词处理和扩展查询

同义词处理是通过允许查询时包括同义词来扩展查询结果的一种方式。SQLiteFTS不直接支持同义词,但是可以通过构建查询时包含同义词来实现。

5.3 文档元数据的排序和过滤

文档元数据指的是文档的附加信息,如标题、作者、发布日期等,它提供了文档的背景信息,有助于改善搜索结果。

5.3.1 元数据字段的添加和使用

在SQLiteFTS中可以为表添加元数据字段,然后根据这些字段对结果进行排序和过滤。

-- 创建FTS表并添加元数据字段
CREATE VIRTUAL TABLE my_table USING fts4(
  title,
  content,
  author,
  date,
  content = 'my_index'
);

-- 示例:根据作者排序
SELECT rowid, title, author, date FROM my_table 
WHERE my_table MATCH 'search query' ORDER BY date DESC;

5.3.2 排序和过滤操作的实现

排序和过滤是提升用户体验的关键,它们帮助用户更快地找到所需信息。

-- 根据日期过滤并排序
SELECT rowid, title, date FROM my_table 
WHERE my_table MATCH 'search query' AND date < '2021-01-01' 
ORDER BY date DESC;

5.4 SQLiteFTS的适用场景与局限性

尽管SQLiteFTS具有多种功能来支持全文搜索,但它并不适用于所有场景,了解其局限性同样重要。

5.4.1 应用SQLiteFTS的典型场景分析

SQLiteFTS适用于小型到中型的数据集,特别是在嵌入式系统、移动应用或桌面应用中。其无需外部依赖和易用性使其成为一个吸引人的选择。

5.4.2 SQLiteFTS的局限性和应对策略

SQLiteFTS的主要局限性在于其性能,对于大型数据集或高并发场景,它可能无法提供足够的性能支持。此外,它对复杂查询的处理能力也有所限制。对于这些场景,可能需要考虑使用更高级的全文搜索解决方案,如Elasticsearch或Apache Solr。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:SQLite作为一个轻量级、自包含的数据库管理系统,在IT领域广泛使用。SQLiteFTS扩展了SQLite,允许开发人员高效地在数据库中实现全文搜索功能,这对处理大量文本数据的应用尤其有用。C#环境下,可以借助SQLite.NET库与SQLite数据库交互,并通过fts4或fts5引擎创建支持全文搜索的表。本指南将展示如何在C#中创建和使用SQLiteFTS,包括创建表、插入数据、执行全文搜索查询,并讨论如何优化搜索结果的相关性。同时,也会考虑实际应用中可能遇到的挑战,如分词、停用词处理等,并指出SQLiteFTS的适用范围及局限性。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值