【Spring Boot】过滤敏感词的两种实现

文章目录

  • 项目场景
  • 前置知识
    • 前缀树
  • 实现方式
    • 解决方案一:读取敏感词文件生成前缀树+构建敏感词过滤器
      • 1. 导入敏感词文件 `src/main/resources/sensitive_words.txt`
      • 2. 构建敏感词过滤器 `SensitiveFilter`
      • 3. 测试与使用
    • 解决方案二:使用第三方插件 `houbb/sensitive-word`(推荐)
      • 1. 添加依赖
      • 2. 测试与使用(使用默认过滤策略)
      • 3. 构建配置类`SensitiveWordConfig`(使用自定义过滤策略)
      • 4. 测试与使用(使用自定义过滤策略)
  • 两种实现方式的对比

项目场景

基于 Spring Boot 的论坛系统:用户发布的内容需进行敏感词过滤。


前置知识

前缀树

  • Q:前缀树和普通N叉树的区别?
  • A:前缀树(Trie,也称字典树、单词查找树或键树)。
    • 设计目的:
      • 前缀树:专门设计用于高效地存储和检索字符串集合中的键(字符串)。它的结构允许快速的查找、插入和删除操作,特别适合于字符串前缀匹配,如自动补全拼写检查敏感词过滤等场景。
      • 普通N叉树:是一种更通用的树形数据结构,其中每个节点可以有任意数量(包括零)的子节点,最多可达N个。它没有特定于字符串处理的特性,广泛应用于各种需要多路分支的数据结构场景,如文件系统的目录结构、表达式树等。
    • 节点结构:
      • 前缀树:每个节点通常包含一个字符和一个映射到其子节点的字符到节点的映射(如HashMap或数组)。根节点通常不表示任何字符,而从根到任意叶节点的路径上的字符序列组成一个字符串,叶节点或标记为关键词结束的节点代表一个完整的字符串。
      • 普通N叉树:节点可能包含数据以及指向其子节点的指针数组或列表,但这些子节点之间的关系不一定有特定的字符关联,节点的数据结构和含义更多样。
    • 查找效率:
      • 前缀树:由于其特殊的结构设计,前缀树支持高效的前缀匹配,可以在O(L)时间内(L为关键词长度)查找到所有具有相同前缀的字符串,或者确定某个字符串是否在集合中。
      • 普通N叉树:查找效率依赖于树的具体形态和查找算法,一般情况下不如前缀树在字符串前缀匹配上的效率高。
    • 空间利用率:
      • 前缀树:可能会有较高的空间消耗,因为它存储了所有字符串的公共前缀,特别是当存储的字符串有很多相似前缀时。
      • 普通N叉树:空间使用更加灵活,取决于树的形状,但通常不会为了存储前缀信息而额外消耗空间。

总之,前缀树是一种针对字符串处理优化的特殊N叉树,强调了字符串前缀的高效存储和查询,而普通N叉树则是一种更为通用的结构,适用于多种类型的多路分支数据组织。


实现方式

解决方案一:读取敏感词文件生成前缀树+构建敏感词过滤器

参考:雨下一整晚Real’s Blog - 【Java项目】社区论坛项目 - 敏感词过滤

前缀树

  • 名称:Trie 、字典树、查找树
  • 特点:查找效率高,消耗内存大
  • 应用:字符串检索、词频统计、字符串排序等

敏感词过滤器

  • 定义前缀树
  • 根据敏感词,初始化前缀树
  • 编写过滤敏感词的方法

1. 导入敏感词文件 src/main/resources/sensitive_words.txt

此处为示例文件内容。

元
购物车
fuck
abc
bf
be

2. 构建敏感词过滤器 SensitiveFilter

package com.example.filter;import com.example.constant.SensitiveConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;/*** 敏感词过滤器*/
@Slf4j
@Component
public class SensitiveFilter {// 创建前缀树的根节点private final TrieNode rootNode = new TrieNode();/*** 初始化,读取敏感词*/@PostConstruct  // 确保在Bean初始化后执行public void init() {// 1. 从类路径加载敏感词文件: 使用文件存储敏感词库可以直接进行一次性读取和处理,无需依赖数据库系统,并且保证离线可用。try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(SensitiveConstant.SENSITIVE_WORDS_FILE);InputStreamReader inputStreamReader = new InputStreamReader(inputStream);BufferedReader bufferedReader = new BufferedReader(inputStreamReader);// Java 7及以后的版本中,引入了一项称为"try-with-resources"的新特性,它允许自动管理资源,确保在try语句块执行完毕后,不论是否发生异常,都会正确关闭或释放资源) {// 2. 逐行读取敏感词并加入到前缀树中String keyword;while ((keyword = bufferedReader.readLine()) != null) {this.addKeyWord(keyword);}} catch (IOException ex) {log.error("敏感词文件读取失败: " + ex.getMessage());}}/*** 将敏感词添加到前缀树中* @param keyword 待添加的敏感词*/private void addKeyWord(String keyword) {// 1. 初始化操作,将当前处理节点设为根节点TrieNode tempNode = rootNode;// 2. 将待添加的敏感词字符串转换为字符数组以便遍历char[] chars = keyword.toCharArray();// 3. 遍历字符数组,逐个字符构建前缀树for (int i = 0; i < chars.length; i++) {// 3.1. 从当前处理节点的子节点Map中获取当前字符对应的节点TrieNode childrenNode = tempNode.getChildrenNode(chars[i]);// 3.1.1 若当前字符没有对应的节点,则创建一个新节点,并放入当前处理节点的子节点Map中if (childrenNode == null) {childrenNode = new TrieNode();tempNode.setChildrenNode(chars[i], childrenNode);}// 3.2. 若当前字符为敏感词字符串的最后一个字符,则标记当前字符的对应节点为敏感词的结尾节点if (i == chars.length - 1) {childrenNode.setKeywordEnd(true);}// 3.3. 移动到下一层,使当前字符对应的节点成为新的当前处理节点,继续构建或遍历过程tempNode = childrenNode;}}/*** 过滤文本,移除或替换其中的敏感词,并返回处理后的文本* @param text 待过滤的文本* @return 过滤后的文本*/public String filter(String text) {// 1. 检查输入文本是否为空或仅包含空白字符,如果是,则直接返回null,表示无内容无需过滤if (StringUtils.isBlank(text)) {return null;}// 2. 初始化变量TrieNode tempNode = rootNode;   // 2.1. 初始化为前缀树的根节点,用于遍历查找。int begin = 0, end = 0;         // 2.2. 分别用于标记待检查文本区间的起始和结束位置StringBuilder result = new StringBuilder(); // 2.3. 累积过滤后的文本// 3. 遍历整个文本进行过滤处理while (begin < text.length()) {char c = text.charAt(end);// 3.1. 遇到符号字符if (isSymbol(c)) {// 3.1.1. 如果当前处于根节点,即没有匹配到任何敏感词的开始,直接保留符号,并移动beginif (tempNode == rootNode) {result.append(c);begin++;}// 3.1.2. 跳过当前符号,并继续检查下一个字符end++;continue;}// 3.2. 检查当前字符区间text[begin...end]是否是某个敏感词的一部分tempNode = tempNode.getChildrenNode(c);// 3.2.1. 如果当前字符没有对应的子节点,即text[begin...end]不是敏感词的组成部分if (tempNode == null) {result.append(text.charAt(begin));  // 3.2.1.1. 保存begin位置的字符到结果end = ++begin;          // 3.2.1.2. 移动begin和end指针,准备检查下一个可能的敏感词 begin++; end = begin;tempNode = rootNode;    // 3.2.1.3. 重置tempNode为根节点准备下一轮匹配}// 3.2.2. 如果当前字符路径已到达一个敏感词的结尾,即text[begin...end]是敏感词else if (tempNode.isKeywordEnd()) {result.append(StringUtils.repeat(SensitiveConstant.REPLACEMENT, end - begin + 1));  // 3.2.2.1. 替换敏感词区间begin = ++end;          // 3.2.2.2. 移动begin和end指针,准备检查下一个可能的敏感词 end++; begin = end;tempNode = rootNode;    // 3.2.2.3. 重置tempNode为根节点准备下一轮匹配}// 3.2.3. 如果当前字符区间text[begin...end]是潜在敏感词的一部分但不是结尾,继续匹配下一个字符else {// 3.2.3.1. 检查下一个字符是否存在,存在则移动end指针;否则,回退begin到当前位置,准备重新匹配if (end < text.length() - 1) {end++;} else {end = begin;}}}// 4. 将剩余未检查的文本(若有)添加到结果中result.append(text.substring(begin));// 5. 返回过滤后的文本return result.toString();}/*** 判断字符是否为符号字符* @param character 待判断的字符* @return true表示是符号字符,需要跳过;false表示不是符号字符,不跳过*/private boolean isSymbol(Character character) {// 0x2E80~0x9FFF是东亚文字: 判断字符是否不属于ASCII字母数字且不在东亚文字范围内return !CharUtils.isAsciiAlphanumeric(character) && (character < 0x2E80 || character > 0x9FFF);}/*** 内部类,定义前缀树的节点*/private static class TrieNode {private boolean isKeywordEnd = false;   // 当前节点是否为敏感词的末尾节点private final Map<Character, TrieNode> childrenNode = new HashMap<>();  // 当前节点的子节点// Getter和Setter方法public boolean isKeywordEnd() {return isKeywordEnd;}public void setKeywordEnd(boolean keywordEnd) {isKeywordEnd = keywordEnd;}public TrieNode getChildrenNode(Character c) {return childrenNode.get(c);}public void setChildrenNode(Character c, TrieNode node) {childrenNode.put(c, node);}}}

这里我把敏感词过滤器的相关常量都写到一个常量类里了,也可以直接写在过滤器里。

package com.example.constant;/*** 敏感词过滤器的相关常量*/
public class SensitiveConstant {/*** 敏感词文件名称*/public static final String SENSITIVE_WORDS_FILE = "sensitive_words.txt";/*** 用于替换敏感词的字符*/public static final Character REPLACEMENT = '*';
}

3. 测试与使用

创建一个测试类测一下就行。

package com.example.filter;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class SensitiveFilterTest {@Autowiredprivate SensitiveFilter sensitiveFilter;@Testvoid filter() {String result = sensitiveFilter.filter("accfuckxx元bitch购物车bitchjwbc");System.out.println(result);}
}

运行结果:

acc****xx*bitch***bitchjwbc

可以实现过滤,但是完全基于敏感词文件内容,需要自行完善文件。也许可以结合正则表达式扩展?不是很灵活,没研究过了。

解决方案二:使用第三方插件 houbb/sensitive-word(推荐)

参考(更多介绍见):常见的敏感词过滤方案汇总以及高效工具sensitive-word快速实践!

houbb/sensitive-word项目源码:https://github.com/houbb/sensitive-word

1. 添加依赖

<dependency><groupId>com.github.houbb</groupId><artifactId>sensitive-word</artifactId><version>0.17.0</version>
</dependency>

2. 测试与使用(使用默认过滤策略)

package com.example.filter;import com.github.houbb.sensitive.word.core.SensitiveWordHelper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class SensitiveFilterTest {@Autowiredprivate SensitiveFilter sensitiveFilter;@Testvoid filter() {String result = sensitiveFilter.filter("accfuckxx元bitch购物车bitchjwbc");System.out.println(result);System.out.println("-------------------------------");result = SensitiveWordHelper.replace("accfuckxx元bitch购物车bitchjwbc");System.out.println(result);result = SensitiveWordHelper.replace("Ⓕⓤc⒦ the bad words");System.out.println(result);result = SensitiveWordHelper.replace("ⒻⒻⒻfⓤuⓤ⒰cⓒ⒦ the bad words");System.out.println(result);result = SensitiveWordHelper.replace("fffuuck the bad words");System.out.println(result);}
}

运行结果:

acc****xx*bitch***bitchjwbc
-------------------------------
acc****xx元bitch购物车bitchjwbc
**** the bad words
ⒻⒻⒻfⓤuⓤ⒰cⓒ⒦ the bad words
fffuuck the bad words

可以实现基础过滤。比读取文件更方便。
(忽略掉这里的“元”和“购物车”。只是为了文章过审把一些很offensive的词换成了随便写的词嗯。)

如果要设置更多需要过滤的内容,可以参考以下步骤。

3. 构建配置类SensitiveWordConfig(使用自定义过滤策略)

自己按照需求调一下就好。

package com.example.config;import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
import com.github.houbb.sensitive.word.support.ignore.SensitiveWordCharIgnores;
import com.github.houbb.sensitive.word.support.resultcondition.WordResultConditions;
import com.github.houbb.sensitive.word.support.tag.WordTags;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 配置类,用于设置敏感词过滤器的自定义过滤策略* 更多配置见 https://github.com/houbb/sensitive-word*/
@Configuration
public class SensitiveWordConfig {/*** 初始化引导类* @return 初始化引导类* @since 1.0.0*/@Beanpublic SensitiveWordBs sensitiveWordBs() {SensitiveWordBs wordBs = SensitiveWordBs.newInstance().ignoreCase(true)           // 忽略大小写,默认值为true.ignoreWidth(true)          // 忽略半角圆角,默认值为true.ignoreNumStyle(true)       // 忽略数字的写法,默认值为true.ignoreChineseStyle(true)   // 忽略中文的书写格式,默认值为true.ignoreEnglishStyle(true)   // 忽略英文的书写格式,默认值为true.ignoreRepeat(false)        // 忽略重复词,默认值为false.enableNumCheck(false)      // 是否启用数字检测,默认值为false.enableEmailCheck(false)    // 是有启用邮箱检测,默认值为false.enableUrlCheck(false)      // 是否启用链接检测,默认值为false.enableIpv4Check(false)     // 是否启用IPv4检测,默认值为false.enableWordCheck(true)      // 是否启用敏感单词检测,默认值为true.numCheckLen(8)             // 数字检测,自定义指定长度,默认值为8.wordTag(WordTags.none())   // 词对应的标签,默认值为none.charIgnore(SensitiveWordCharIgnores.defaults())            // 忽略的字符,默认值为none.wordResultCondition(WordResultConditions.alwaysTrue())     // 针对匹配的敏感词额外加工,比如可以限制英文单词必须全匹配,默认恒为真.init();return wordBs;}
}

4. 测试与使用(使用自定义过滤策略)

在刚刚的配置类中已经启用了数字检测。

.enableNumCheck(true)      // 是否启用数字检测,默认值为false

测一下。

package com.example.filter;import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
import com.github.houbb.sensitive.word.core.SensitiveWordHelper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class SensitiveFilterTest {...@Autowiredprivate SensitiveWordBs sensitiveWordBs;...@Testvoid test() {String result = sensitiveWordBs.replace("accfuckxx元bitch购物车bitchjwbc");System.out.println(result);result = sensitiveWordBs.replace("Ⓕⓤc⒦ the bad words");System.out.println(result);result = sensitiveWordBs.replace("ⒻⒻⒻfⓤuⓤ⒰cⓒ⒦ the bad words");System.out.println(result);result = sensitiveWordBs.replace("fffuuck the bad words");System.out.println(result);result = sensitiveWordBs.replace("12345678dwnoxcw");System.out.println(result);result = sensitiveWordBs.replace("123456789dwnoxcw");System.out.println(result);result = sensitiveWordBs.replace("一二三四五六七八九dwnoxcw");System.out.println(result);result = sensitiveWordBs.replace("这个是我的微信:9⓿二肆⁹₈③⑸⒋➃㈤㊄");System.out.println(result);}
}

运行结果:

acc****xx元bitch购物车bitchjwbc
**** the bad words
*********** the bad words
******* the bad words
********dwnoxcw
*********dwnoxcw
*********dwnoxcw
这个是我的微信:************

就很好用了。
(忽略掉这里的“元”和“购物车”。只是为了文章过审把一些很offensive的词换成了随便写的词嗯。)


两种实现方式的对比

  • 自行构建前缀树过滤器:
    • 优势:高度定制,易于理解与维护,无外部依赖。
    • 劣势:开发耗时,需优化性能,学习成本。
  • 使用第三方开源项目:
    • 优势:快速集成,功能成熟,社区支持。
    • 劣势:依赖管理,安全风险,定制受限。

根据项目需求紧迫性、定制化需求及团队技术背景综合选择。简单需求或有定制化要求倾向自建;追求快速、功能全面则推荐使用现成开源方案。

最后。全文仅做学习交流使用,参考来源均已标明。
感谢提供开源的大佬们。

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

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

相关文章

Prisma数据库ORM框架学习

初始化项目 中文网站 点击快速开始,点击创建sql项目,后面一步一步往后走 这个博主也挺全的,推荐下 可以看这个页面初始化项目跟我下面是一样的,这里用得是ts,我下面是js,不需要额外的配置了 1.vscode打开一个空文件夹 2.npm init -y 初始化package.json 3.安装相关依赖 …

「前端+鸿蒙」鸿蒙应用开发-TS接口-语法多态

在 TypeScript 中&#xff0c;接口&#xff08;Interfaces&#xff09;是一种强大的方式来定义对象的结构&#xff0c;它们用于类型检查和确保对象符合特定的结构。接口也可以用于类&#xff0c;实现类似多态的特性。以下是 TypeScript 中接口的语法和多态的快速入门指南。 TS快…

常用的通信协议

最近在做项目&#xff0c;用到了一些通信协议&#xff0c;这里详细整理一下相关的通信协议&#xff0c;方便以后查阅。 常用的通信协议 单工 半双工 全双工单工通信&#xff08;Simplex Communication&#xff09;半双工(Half-duplex Communication)全双工&#xff08;Full-dup…

速卖通如何放关联?

大家都知道&#xff0c;想要进行多账号操作必须一再小心&#xff0c;否则会有很大的关联风险&#xff0c;而账号关联所带来的后果是卖家绝对不能轻视的&#xff0c;严重的话会导致封号&#xff0c;这样一来自己前期的辛苦运营就全都打水漂了&#xff0c;因此防关联很重要&#…

Python框架scrapy有什么天赋异禀

Scrapy框架与一般的爬虫代码之间有几个显著的区别&#xff0c;这些差异主要体现在设计模式、代码结构、执行效率以及可扩展性等方面。下面是一些关键的不同点&#xff1a; 结构化与模块化&#xff1a; Scrapy&#xff1a;提供了高度结构化的框架&#xff0c;包括定义好的Spider…

MySQL—多表查询—小结

一、引言 前面的博客已经全部学习完了关于多表查询。接下来对多表查询进行一个小结。 &#xff08;1&#xff09;多表查询主要是讲了两个方面 多表关系 &#xff08;不管业务关系如何的复杂&#xff0c;最终多表的关系基本上可以分为三类&#xff09; "一对多"、&qu…

大数据技术Hbase列数据库——topic3

目录 启动Hadoop启动HbaseHbase常用Shell命令基本命令关于表的操作增删改查询 启动Hadoop 1.到Hadoop安装目录下输入命令 sbin/start-all.sh[rootlocalhost hadoop-2.7.1]# sbin/start-all.sh This script is Deprecated. Instead use start-dfs.sh and start-yarn.sh Starti…

《Vue》系列文章目录

Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0c;Vue 都可以…

载波相移CPS-SPWM调制方法的simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 载波相移CPS-SPWM调制方法的simulink建模与仿真&#xff0c;载波相移PWM方法&#xff1a; 2.系统仿真结果 单极倍频 釆用 调制波 反相 法 &#xff0c; 基本调制原理为 &…

Golang 避坑指南

文章目录 1. Channel 与 Goroutine 泄露1.1 发送不接收1.2 接收不发送1.3 nil channel2. 跳出 for-switch 或 for-select 3.for 迭代变量3.1 闭包中的for迭代变量3.2 for range 迭代变量 4. 循环内的 defer5.defer 函数的参数值6.nil interface 和 nil interface 值7.结构体指针…

【Bazel入门与精通】 rules之属性

https://bazel.build/extending/rules?hlzh-cn#attributes Attributes An attribute is a rule argument. Attributes can provide specific values to a target’s implementation, or they can refer to other targets, creating a graph of dependencies. Rule-specifi…

B3810 [语言月赛 202307] 扶苏和串

[语言月赛 202307] 扶苏和串 题目背景 众所周知&#xff0c;每个月入门赛的字符串题都是扶苏来枚举 idea 出出来的。 题目描述 给定一个 01 字符串 s s s&#xff0c;你可以任选 s s s 的一个非空子串&#xff0c;把这个子串在 s s s 中翻转一次。 问你能得到字典序最小…

R语言:使用 stringr 包进行字符串处理和正则表达式匹配

在 R 编程中&#xff0c;字符串处理和正则表达式匹配是常见的需求&#xff0c;而 stringr 包提供了强大的工具来简化这些操作。本文将介绍 stringr 包的基础用法&#xff0c;并结合正则表达式的相关内容&#xff0c;帮助你高效处理字符串数据。 字符串基础 创建字符串 在 R …

大模型训练数据白皮书深度解读

摘要 随着人工智能技术的飞速发展&#xff0c;大模型在各个领域的应用日益广泛。《大模型训练数据白皮书》为我们提供了对大模型训练数据重要性的深刻理解&#xff0c;本文将深度解读白皮书的主要内容&#xff0c;探讨大模型训练数据的关键要素、面临的挑战与未来发展趋势。 …

金融量化分析开源工具:TuShare

TuShare&#xff1a;一站式金融数据解决方案&#xff0c;让量化分析触手可及- 精选真开源&#xff0c;释放新价值。 概览 TuShare&#xff0c;是Github社区上一个专为金融量化分析师和数据爱好者设计的开源工具&#xff0c;提供了从数据采集、清洗加工到数据存储的全流程服务。…

缓存更新策略中级总结

背景 看到好些人在写更新缓存数据代码时&#xff0c;先删除缓存&#xff0c;然后再更新数据库&#xff0c;而后续的操作会把数据再装载的缓存中。然而&#xff0c;这个是逻辑是错误的。试想&#xff0c;两个并发操作&#xff0c;一个是更新操作&#xff0c;另一个是查询操作…

构建大语言模型友好型网站

以大语言模型为代表的AI 技术迅速发展&#xff0c;将会影响原有信息网络的方式。其中一个明显的趋势是通过chatGPT 对话代替搜索引擎和浏览器来获取信息。 互联网时代&#xff0c;主要是通过网站&#xff08;website&#xff09;提供信息。网站主要为人类阅读的方式构建的。主要…

高通Android开关机动画踩坑简单记录

1、下面报错有可能是selinux的原因 Read-only file system 2、接着push 动画 reboot之后抓取logcat出现 以下报错。看着大概意思像是压缩格式有问题。 3、于是重新压缩一下报错没有再出现 &#xff0c;压缩格式默认是标准&#xff0c;这里必须要改成存储格式哈 4、修改之后重新…

GitHub工程git merge出现冲突处理方式

GitHub工程git merge出现冲突处理方式 1. 源由2. 冲突解决2.1 触发合并冲突2.2 查看冲突状态2.3 打开冲突文件2.4 解决冲突2.5 标记冲突已解决2.6 继续合并2.7 检查合并结果 3. 示例 1. 源由 在产品研发中&#xff0c;常见的技术动作就是merge&#xff0c;而这个操作通常有两种…

Mysql基础 - 事务

Mysql基础 - 事务 文章目录 Mysql基础 - 事务1 事务简介2 事务操作2.1 控制事务一2.2 控制事务二 3 事务四大特性4 并发事务问题5 事务隔离级别 1 事务简介 事务是一组操作的集合&#xff0c;他是一个不可分割的工作单位&#xff0c;事务会把所有操作作为一个整体一起向系统提…