首次提交:初始化项目代码

This commit is contained in:
sunct
2026-07-31 15:18:32 +08:00
commit d6393266b0
193 changed files with 14287 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
1、app.log的测试文本内容2026年6月12日,邮箱:demo@miaoall.cn
2、app.log的测试文本内容2026年6月12日,备用邮箱:admin@miaoall.cn
+18
View File
@@ -0,0 +1,18 @@
package main
import (
"fmt"
"regexp"
)
// 全局仅编译一次,全局复用,性能最优
//var pattern = `^1[3-9]\d{9}$`
// 更为严格的匹配
var pattern = `^1(3[0-9]|4[5-9]|5[0-35-9]|6[2567]|7[0-9]|8[0-9]|9[0-35-9])[0-9]{8}$`
var phoneRegex = regexp.MustCompile(pattern)
func main() {
fmt.Println("合法手机号校验:", phoneRegex.MatchString("13812345678"))
fmt.Println("非法手机号校验:", phoneRegex.MatchString("11111122222"))
}
+58
View File
@@ -0,0 +1,58 @@
package main
import (
"fmt"
"regexp"
"unicode/utf8"
)
func main() {
// 测试场景1
text1 := "余额200,积分50,代金券80"
// 测试场景2:两个50,一个积分、一个代金券
text2 := "余额200,积分50,代金券50"
fmt.Println("场景1结果:", filterNotScoreNum(text1))
fmt.Println("场景2结果:", filterNotScoreNum(text2))
}
// filterNotScoreNum 过滤:数字前面紧邻是“积分”的数字剔除,等价 (?!积分) 反向语义
func filterNotScoreNum(text string) []string {
numRe := regexp.MustCompile(`\d+`)
indexList := numRe.FindAllStringIndex(text, -1)
var validNums []string
for _, pos := range indexList {
startIdx, endIdx := pos[0], pos[1]
numStr := text[startIdx:endIdx]
// 读取数字前面的上下文,倒推判断前面是不是“积分”
// 从数字开头位置向前读取2个汉字的符文长度
beforeStr := text[:startIdx]
// 倒着解码两个rune
runeCnt := 0
var lastTwoRunes []rune
// 反向遍历符文
for i := len(beforeStr); i > 0 && runeCnt < 2; {
r, size := utf8.DecodeLastRuneInString(beforeStr[:i])
lastTwoRunes = append([]rune{r}, lastTwoRunes...)
i -= size
runeCnt++
}
// 判断数字前两个汉字是否为“积分”
isScoreNum := false
if len(lastTwoRunes) == 2 {
prefix := string(lastTwoRunes)
if prefix == "积分" {
isScoreNum = true
}
}
// 不是积分对应的数字才保留
if !isScoreNum {
validNums = append(validNums, numStr)
}
}
return validNums
}
+52
View File
@@ -0,0 +1,52 @@
package main
import (
"fmt"
"regexp"
"unicode"
)
// checkDigitSuffix 等价原 \d+(?!\D+$)
// 入参:完整字符串、一段数字的结束下标
// 返回:true = 符合条件(数字后面不全是字母)
func checkDigitSuffix(s string, digitEnd int) bool {
// 数字后面无任何字符,直接不符合
if digitEnd >= len(s) {
return false
}
after := s[digitEnd:]
// 遍历数字之后所有字符
allLetter := true
for _, r := range after {
// 判断是否大小写字母
if !unicode.IsLetter(r) {
allLetter = false
break
}
}
// 不全是字母 → 返回true匹配
return !allLetter
}
func main() {
pwdList := []string{"abc123", "123abc", "abcdef", "x99!yy", "789#"}
digitRe := regexp.MustCompile(`\d+`)
fmt.Println("密码\t匹配结果")
fmt.Println("----------------")
for _, pwd := range pwdList {
// 找出所有数字段
allDigitIdx := digitRe.FindAllStringIndex(pwd, -1)
matchFlag := false
for _, idx := range allDigitIdx {
endPos := idx[1]
if checkDigitSuffix(pwd, endPos) {
matchFlag = true
break
}
}
fmt.Printf("%s\t%t\n", pwd, matchFlag)
}
}
+42
View File
@@ -0,0 +1,42 @@
package main
import (
"fmt"
"regexp"
)
func main() {
// 场景1:Longest 长短匹配对比(最核心模式切换)
fmt.Println("===== 1. Longest最长匹配 vs 默认first匹配 =====")
expr := `(a+)|(aaa)`
// 默认 leftmost-first
reDefault := regexp.MustCompile(expr)
fmt.Println("默认模式匹配aaaa", reDefault.FindString("aaaa")) // 优先第一个分支a+,短匹配
// 开启最长匹配
reLong := regexp.MustCompile(expr)
reLong.Longest()
fmt.Println("Longest最长模式匹配aaaa", reLong.FindString("aaaa"))
// 场景2. 匹配换行 RE2专属 (?s:.)
fmt.Println("\n===== 2. 点号匹配换行符 =====")
strWithLine := "hello\nworld"
// 普通. 不跨换行
reDotNormal := regexp.MustCompile(`hello.world`)
fmt.Println("普通.匹配换行:", reDotNormal.MatchString(strWithLine))
// (?s:.) 让.识别换行
reDotAll := regexp.MustCompile(`(?s:hello.world)`)
fmt.Println("(?s:.)开启跨行匹配:", reDotAll.MatchString(strWithLine))
// 场景3:大小写不敏感匹配(无修饰符,字符组兼容大小写)
fmt.Println("\n===== 3. 大小写忽略匹配方案 =====")
target := "Apple APPLE apple aPpLe"
// 写法1:字符组罗列大小写
reCase := regexp.MustCompile(`[Aa][Pp][Pp][Ll][Ee]`)
fmt.Println("大小写全部匹配结果:", reCase.FindAllString(target, -1))
// 场景4CompilePOSIX 天生Longest语义
fmt.Println("\n===== 4. CompilePOSIX 原生最长匹配 =====")
rePosix, _ := regexp.CompilePOSIX(expr)
fmt.Println("CompilePOSIX匹配aaaa", rePosix.FindString("aaaa"))
}
+83
View File
@@ -0,0 +1,83 @@
package main
import (
"bufio"
"fmt"
"os"
"regexp"
)
// 通用邮箱正则
var emailRegex = regexp.MustCompile(`[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}`)
func main() {
// 测试文本:复用文档本地测试域名,零外网依赖
content := "工作邮箱:demo@miaoall.cn,备用邮箱:admin@miaoall.cn,网站地址:https://www.miaoall.cn"
contentBytes := []byte(content) // 字节流数据,适配[]byte系列API
fmt.Println("========== 1. 基础匹配校验API ==========")
// 字符串匹配校验
fmt.Println("字符串是否含邮箱(MatchString)", emailRegex.MatchString(content))
// 字节流匹配校验
fmt.Println("字节流是否含邮箱(Match)", emailRegex.Match(contentBytes))
fmt.Println("\n========== 2. 单次匹配内容/下标API ==========")
// 单次字符串匹配内容
firstEmail := emailRegex.FindString(content)
fmt.Println("首个匹配邮箱(FindString)", firstEmail)
// 单次字符串匹配下标
firstIndex := emailRegex.FindStringIndex(content)
fmt.Println("首个邮箱起止下标(FindStringIndex)", firstIndex)
// 单次字节流匹配内容、下标
firstByteEmail := emailRegex.Find(contentBytes)
fmt.Println("字节流首个邮箱(Find)", string(firstByteEmail))
firstByteIndex := emailRegex.FindIndex(contentBytes)
fmt.Println("字节流首个邮箱下标(FindIndex)", firstByteIndex)
fmt.Println("\n========== 3. 批量匹配内容/下标API ==========")
// 批量匹配所有字符串结果
allEmail := emailRegex.FindAllString(content, -1)
fmt.Println("全部邮箱(FindAllString)", allEmail)
// 批量匹配所有下标
allIndex := emailRegex.FindAllStringIndex(content, -1)
fmt.Println("全部邮箱下标(FindAllStringIndex)", allIndex)
// 字节流批量匹配
allByteEmail := emailRegex.FindAll(contentBytes, -1)
fmt.Print("字节流全部邮箱(FindAll)")
for _, v := range allByteEmail {
fmt.Print(string(v), " ")
}
fmt.Println()
fmt.Println("\n========== 4. 基础捕获组匹配API ==========")
// 复用链接正则,测试子匹配
var urlRegex = regexp.MustCompile(`(?:http|https)://(\w+\.\w+\.\w+)`)
// 单次字符串子匹配
subMatch := urlRegex.FindStringSubmatch(content)
fmt.Println("单次子匹配结果(FindStringSubmatch)", subMatch)
// 单次子匹配下标
subMatchIndex := urlRegex.FindStringSubmatchIndex(content)
fmt.Println("单次子匹配下标(FindStringSubmatchIndex)", subMatchIndex)
// 批量子匹配、下标
allSubMatch := urlRegex.FindAllStringSubmatch(content, -1)
fmt.Println("批量子匹配(FindAllStringSubmatch)", allSubMatch)
allSubIndex := urlRegex.FindAllStringSubmatchIndex(content, -1)
fmt.Println("批量子匹配下标(FindAllStringSubmatchIndex)", allSubIndex)
fmt.Println("\n========== 5. 文本分割API ==========")
// 按邮箱分割原文本
splitRes := emailRegex.Split(content, -1)
fmt.Println("正则分割文本结果(Split)", splitRes)
fmt.Println("\n========== 6. 流式读取API(防OOM ==========")
// 流式读取适配超大文件,逐符文读取,杜绝全量加载内存溢出
file, err := os.Open("regexp/app.log")
if err != nil {
fmt.Println("流式文件读取模拟(无文件则跳过):", err)
} else {
defer file.Close()
// bufio.Reader 实现 io.RuneReader 接口,可用于所有流式正则API
reader := bufio.NewReader(file)
fmt.Println("流式匹配是否含邮箱(MatchReader)", emailRegex.MatchReader(reader))
}
}
+19
View File
@@ -0,0 +1,19 @@
package main
import (
"fmt"
"regexp"
)
var userRegex = regexp.MustCompile(`姓名:([\p{Han}a-zA-Z0-9]+),年龄:(\d+),电话:(\d{11})`)
func main() {
text := "用户信息:姓名:孙三苗,年龄:20,电话:11112345678" // 号码不存在仅供测试
match := userRegex.FindStringSubmatch(text)
if len(match) > 0 {
fmt.Println("完整匹配:", match[0])
fmt.Println("姓名:", match[1])
fmt.Println("年龄:", match[2])
fmt.Println("电话:", match[3])
}
}
+18
View File
@@ -0,0 +1,18 @@
package main
import (
"fmt"
"regexp"
)
var namedRegex = regexp.MustCompile(`姓名:(?P<name>[\p{Han}a-zA-Z0-9]+),城市:(?P<city>[\p{Han}a-zA-Z0-9]+)`)
func main() {
text := "姓名:孙三苗,城市:北京"
match := namedRegex.FindStringSubmatch(text)
for i, name := range namedRegex.SubexpNames() {
if i > 0 && name != "" {
fmt.Printf("%s%s\n", name, match[i])
}
}
}
+15
View File
@@ -0,0 +1,15 @@
package main
import (
"fmt"
"regexp"
)
// 匹配 http/https 协议链接,不单独捕获协议字段
var urlRegex = regexp.MustCompile(`(?:http|https)://\w+\.\w+\.\w+`)
func main() {
text := "站点:https://www.miaoall.cn 镜像:http://www.miaoall.cn"
res := urlRegex.FindAllString(text, -1)
fmt.Println("匹配链接:", res)
}
+57
View File
@@ -0,0 +1,57 @@
package main
import (
"fmt"
"regexp"
"strconv"
)
func main() {
// 1、字符串反向引用脱敏 ReplaceAllString
mobileRe := regexp.MustCompile(`(\d{3})\d{4}(\d{4})`)
phone := "12311111111"
// $$ 转义输出单个字面$
res1 := mobileRe.ReplaceAllString(phone, "前缀$$ $1****$2 后缀")
fmt.Println("1.ReplaceAllString 手机号脱敏+$转义:", res1)
// 命名捕获组搭配${name}格式
nameRe := regexp.MustCompile(`姓名:(?P<name>[\p{Han}]+)`)
userText := "姓名:王五"
resName := nameRe.ReplaceAllString(userText, "用户${name}已登记")
fmt.Println("命名捕获组替换:", resName)
// 2、字面量&常规替换核心对比(带捕获组,差异肉眼可见)
litStr := "价格$100,优惠$20"
numRe := regexp.MustCompile(`(\d+)`)
res2Normal := numRe.ReplaceAllString(litStr, "数值$1")
res2Lit := numRe.ReplaceAllLiteralString(litStr, "数值$1")
fmt.Println("2.ReplaceAllString 解析捕获组$1", res2Normal)
fmt.Println("2.ReplaceAllLiteralString 纯字面$1", res2Lit)
// 3、动态回调替换 ReplaceAllStringFunc
priceText := "金额:10、20、50"
res3 := numRe.ReplaceAllStringFunc(priceText, func(s string) string {
val, _ := strconv.Atoi(s)
return strconv.Itoa(val * 2)
})
fmt.Println("3.ReplaceAllStringFunc 数值翻倍:", res3)
// ---------------------- []byte 字节系列替换API ----------------------
bytePhone := []byte("12311111111")
// 4.ReplaceAll 字节版反向引用
resByte1 := mobileRe.ReplaceAll(bytePhone, []byte("$1****$2"))
fmt.Println("4.ReplaceAll 字节脱敏:", string(resByte1))
// 5.ReplaceAllLiteral 字节字面量演示
byteSrc := []byte("积分30")
resByte2 := numRe.ReplaceAllLiteral(byteSrc, []byte("分数$1"))
fmt.Println("5.ReplaceAllLiteral 字节字面量:", string(resByte2))
// 6.ReplaceAllFunc 字节回调自定义逻辑
bytePrice := []byte("标价5 标价15")
resByte3 := numRe.ReplaceAllFunc(bytePrice, func(b []byte) []byte {
val, _ := strconv.Atoi(string(b))
return []byte(strconv.Itoa(val + 10))
})
fmt.Println("6.ReplaceAllFunc 字节数值加10", string(resByte3))
}
+182
View File
@@ -0,0 +1,182 @@
package main
import (
"fmt"
"regexp"
"strings"
)
func main() {
fmt.Println("=== Golang正则表达式ReplaceAllLiteralString使用示例 ===")
// 1. 基本用法演示
fmt.Println("\n1. 基本用法演示")
text := "Hello world! This is a world of Go programming. Welcome to the world!"
// 使用普通替换
normalReplace := regexp.MustCompile(`world`).ReplaceAllString(text, "universe")
fmt.Printf("原始文本: %s\n", text)
fmt.Printf("普通替换(ReplaceAllString): %s\n", normalReplace)
// 使用字面量替换
literalReplace := regexp.MustCompile(`world`).ReplaceAllLiteralString(text, "universe")
fmt.Printf("字面量替换(ReplaceAllLiteralString): %s\n", literalReplace)
// 2. 特殊字符处理对比
fmt.Println("\n2. 特殊字符处理对比")
specialText := "Price: $100, Discount: $20, Total: $80"
// 普通替换 - $有特殊含义
normalResult := regexp.MustCompile(`\$`).ReplaceAllString(specialText, "USD ")
fmt.Printf("特殊字符 - 普通替换: %s\n", normalResult)
// 字面量替换 - $被视为普通字符
literalResult := regexp.MustCompile(`\$`).ReplaceAllLiteralString(specialText, "USD ")
fmt.Printf("特殊字符 - 字面量替换: %s\n", literalResult)
// 3. 捕获组符号处理
fmt.Println("\n3. 捕获组符号处理")
groupText := "Pattern: (abc) and [def] with {ghi}"
// 普通替换 - $1, $2等会被当作捕获组引用
normalGroup := regexp.MustCompile(`\(([^\)]+)\)`).ReplaceAllString(groupText, "GROUP($1)")
fmt.Printf("捕获组 - 普通替换: %s\n", normalGroup)
// 字面量替换 - $符号被当作普通字符处理
literalGroup := regexp.MustCompile(`\(([^\)]+)\)`).ReplaceAllLiteralString(groupText, "GROUP($1)")
fmt.Printf("捕获组 - 字面量替换: %s\n", literalGroup)
// 4. 实际应用场景 - HTML转义
fmt.Println("\n4. 实际应用场景 - HTML转义")
htmlText := "<div class='test'>Click & go to \"page\"</div>"
// 正确的HTML转义方法 - 使用ReplaceAllStringFunc
htmlReplacer := regexp.MustCompile(`[<>&"']`)
escapedHTML := htmlReplacer.ReplaceAllStringFunc(htmlText, func(match string) string {
switch match {
case "<":
return "&lt;"
case ">":
return "&gt;"
case "&":
return "&amp;"
case "\"":
return "&quot;"
case "'":
return "&#x27;"
default:
return match
}
})
fmt.Printf("HTML转义前: %s\n", htmlText)
fmt.Printf("HTML转义后: %s\n", escapedHTML)
// 5. SQL注入防护
fmt.Println("\n5. SQL注入防护示例")
sqlText := "SELECT * FROM users WHERE name = 'admin'; DROP TABLE users; --'"
// 移除危险字符 - 使用ReplaceAllLiteralString
sqlSafe := regexp.MustCompile(`['";--]`).ReplaceAllLiteralString(sqlText, "_")
fmt.Printf("原始SQL: %s\n", sqlText)
fmt.Printf("净化后SQL: %s\n", sqlSafe)
// 6. 路径规范化
fmt.Println("\n6. 路径规范化")
pathText := "/home/user/../documents/./files//temp/"
// 标准化路径分隔符
normalizedPath := regexp.MustCompile(`/+`).ReplaceAllLiteralString(pathText, "/")
fmt.Printf("原始路径: %s\n", pathText)
fmt.Printf("标准化路径: %s\n", normalizedPath)
// 7. 用户输入清理
fmt.Println("\n7. 用户输入清理")
userInput := "User input with <script>alert('xss')</script> and other dangerous content"
// 移除潜在危险标签
cleanInput := regexp.MustCompile(`<script[^>]*>.*?</script>`).ReplaceAllLiteralString(userInput, "")
fmt.Printf("原始输入: %s\n", userInput)
fmt.Printf("清理后输入: %s\n", cleanInput)
// 8. 电话号码格式化
fmt.Println("\n8. 电话号码格式化")
phoneText := "Call me at 1234567890 or 0987654321 for more info"
// 正确的格式化方法 - 使用ReplaceAllString(因为需要捕获组引用)
correctFormattedPhone := regexp.MustCompile(`(\d{3})(\d{3})(\d{4})`).ReplaceAllString(phoneText, "$1-$2-$3")
fmt.Printf("原始号码: %s\n", phoneText)
fmt.Printf("格式化号码: %s\n", correctFormattedPhone)
// 9. 敏感信息脱敏
fmt.Println("\n9. 敏感信息脱敏")
sensitiveText := "Email: john@example.com, Phone: 123-456-7890, SSN: 123-45-6789"
// 脱敏邮箱 - 使用ReplaceAllString(需要捕获组)
emailMasked := regexp.MustCompile(`([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})`).ReplaceAllString(sensitiveText, "***@***.com")
fmt.Printf("原始信息: %s\n", sensitiveText)
fmt.Printf("邮箱脱敏: %s\n", emailMasked)
// 10. 关键词过滤
fmt.Println("\n10. 关键词过滤")
badWordsText := "This contains some bad words like crap, damn, and hell that should be filtered out"
// 过滤不良词汇
filteredText := regexp.MustCompile(`(?i)(crap|damn|hell)`).ReplaceAllLiteralString(badWordsText, "***")
fmt.Printf("原文本: %s\n", badWordsText)
fmt.Printf("过滤后: %s\n", filteredText)
// 11. 价格格式化
fmt.Println("\n11. 价格格式化")
priceText := "Product A costs $29.99, Product B costs $199.99"
// 将美元符号替换为人民币符号 - 使用ReplaceAllLiteralString
rmbPrice := regexp.MustCompile(`\$`).ReplaceAllLiteralString(priceText, "¥")
fmt.Printf("原始价格: %s\n", priceText)
fmt.Printf("人民币价格: %s\n", rmbPrice)
// 12. 批量替换示例
fmt.Println("\n12. 批量替换示例")
batchText := "Replace @user with [USERNAME] and #hashtag with [HASHTAG]"
// 替换多个模式
result := batchText
result = regexp.MustCompile(`@(\w+)`).ReplaceAllLiteralString(result, "[USER]")
result = regexp.MustCompile(`#(\w+)`).ReplaceAllLiteralString(result, "[TAG]")
fmt.Printf("原始文本: %s\n", batchText)
fmt.Printf("批量替换后: %s\n", result)
// 13. 特殊字符处理
fmt.Println("\n13. 特殊字符处理")
specialCharsText := "This has special chars: $10, \\path, $variable, and \\n newlines"
// 处理包含$和\的文本 - 使用ReplaceAllLiteralString确保特殊字符被正确处理
safeReplace := regexp.MustCompile(`special chars: \$10, \\path`).ReplaceAllLiteralString(specialCharsText, "SAFE TEXT")
fmt.Printf("原始文本: %s\n", specialCharsText)
fmt.Printf("安全替换后: %s\n", safeReplace)
// 14. 性能对比演示
fmt.Println("\n14. 性能对比演示")
longText := strings.Repeat("This is a test string with pattern to replace. ", 1000)
// 测试ReplaceAllLiteralString性能
re1 := regexp.MustCompile(`pattern`)
// 由于无法直接测试性能,这里展示用法
literalResult = re1.ReplaceAllLiteralString(longText, "replacement")
fmt.Printf("ReplaceAllLiteralString处理长文本完成,结果长度: %d\n", len(literalResult))
// 15. 实际应用:日志清理
fmt.Println("\n15. 实际应用:日志清理")
logText := "INFO: User login success for admin@company.com at 2026-06-15 10:30:45"
// 清理敏感信息
cleanLog := regexp.MustCompile(`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`).ReplaceAllLiteralString(logText, "[EMAIL_REDACTED]")
fmt.Printf("原始日志: %s\n", logText)
fmt.Printf("清理后日志: %s\n", cleanLog)
fmt.Println("\n=== ReplaceAllLiteralString vs ReplaceAllString 对比 ===")
fmt.Println("ReplaceAllString: 替换字符串中的$符号有特殊含义,用于引用捕获组")
fmt.Println("ReplaceAllLiteralString: 替换字符串中的$符号被当作普通字符处理,不会被解释为捕获组引用")
fmt.Println("使用场景: 当替换字符串包含$、\\等特殊字符且不想被解释时,使用ReplaceAllLiteralString")
fmt.Println("\n注意: 如果需要使用捕获组功能,应使用ReplaceAllString或ReplaceAllStringFunc")
}
+13
View File
@@ -0,0 +1,13 @@
package main
import (
"fmt"
"regexp"
)
func main() {
html := "<div>内容1</div><div>内容2</div>"
// 贪婪匹配会一次性匹配到最后闭合标签,导致合并结果
greedy := regexp.MustCompile(`<div>(.*)</div>`)
fmt.Println("贪婪匹配错误结果:", greedy.FindAllString(html, -1))
}
+12
View File
@@ -0,0 +1,12 @@
package main
import (
"fmt"
"regexp"
)
func main() {
html := "<div>内容1</div><div>内容2</div>"
lazy := regexp.MustCompile(`<div>(.*?)</div>`)
fmt.Println("非贪婪匹配正确结果:", lazy.FindAllString(html, -1))
}
+20
View File
@@ -0,0 +1,20 @@
package main
import (
"fmt"
"regexp"
)
func main() {
text := "A:99元,B:199元,积分:50"
// 捕获【数字+元】整体,分组单独取出数字
re := regexp.MustCompile(`(\d+)元`)
matches := re.FindAllStringSubmatch(text, -1)
var amountList []string
for _, item := range matches {
// item[0] = 99元,item[1] = 99 纯数字
amountList = append(amountList, item[1])
}
fmt.Println("仅提取金额数字:", amountList)
}