tools.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package main
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. )
  7. type RuleDetail struct {
  8. RuleRaw string
  9. RequiredMatched []string
  10. ExcludedMatched []string
  11. Matched bool
  12. }
  13. type MatchResult struct {
  14. Matched bool
  15. MatchedRules []string
  16. FailedRules []string
  17. Details []RuleDetail
  18. }
  19. func parseRule(rule string) (required []string, minCount int, excluded []string) {
  20. rule = strings.TrimSpace(rule)
  21. if strings.Contains(rule, "^") {
  22. // 复合规则 (A|B|C)N^(X|Y|Z)
  23. re := regexp.MustCompile(`^\(([^)]+)\)(\d+)\^\(([^)]+)\)$`)
  24. matches := re.FindStringSubmatch(rule)
  25. if len(matches) != 4 {
  26. panic("复合规则格式错误:" + rule)
  27. }
  28. required = strings.Split(matches[1], "|")
  29. minCount = atoi(matches[2])
  30. excluded = strings.Split(matches[3], "|")
  31. } else {
  32. // 简单规则 (A|B|C)
  33. re := regexp.MustCompile(`^\(([^)]+)\)$`)
  34. matches := re.FindStringSubmatch(rule)
  35. if len(matches) != 2 {
  36. panic("简单规则格式错误:" + rule)
  37. }
  38. required = strings.Split(matches[1], "|")
  39. minCount = 1
  40. excluded = nil
  41. }
  42. return
  43. }
  44. func atoi(s string) int {
  45. var i int
  46. fmt.Sscanf(s, "%d", &i)
  47. return i
  48. }
  49. func matchSingleRule(text string, rule string) RuleDetail {
  50. required, minCount, excluded := parseRule(rule)
  51. detail := RuleDetail{
  52. RuleRaw: rule,
  53. }
  54. for _, kw := range required {
  55. if strings.Contains(text, kw) {
  56. detail.RequiredMatched = append(detail.RequiredMatched, kw)
  57. }
  58. }
  59. for _, ex := range excluded {
  60. if strings.Contains(text, ex) {
  61. detail.ExcludedMatched = append(detail.ExcludedMatched, ex)
  62. }
  63. }
  64. detail.Matched = len(detail.RequiredMatched) >= minCount && len(detail.ExcludedMatched) == 0
  65. return detail
  66. }
  67. func MatchAllRules(text string, ruleStr string) MatchResult {
  68. rules := splitRules(ruleStr)
  69. res := MatchResult{}
  70. for _, rule := range rules {
  71. detail := matchSingleRule(text, rule)
  72. res.Details = append(res.Details, detail)
  73. if detail.Matched {
  74. res.MatchedRules = append(res.MatchedRules, rule)
  75. } else {
  76. res.FailedRules = append(res.FailedRules, rule)
  77. }
  78. }
  79. res.Matched = len(res.MatchedRules) > 0
  80. return res
  81. }
  82. // 支持用逗号分割时括号内可能也有逗号的情况
  83. func splitRules(s string) []string {
  84. var res []string
  85. start := 0
  86. level := 0
  87. for i, c := range s {
  88. if c == '(' {
  89. level++
  90. } else if c == ')' {
  91. level--
  92. } else if c == ',' && level == 0 {
  93. res = append(res, strings.TrimSpace(s[start:i]))
  94. start = i + 1
  95. }
  96. }
  97. res = append(res, strings.TrimSpace(s[start:]))
  98. return res
  99. }