1
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// NormalizeKgTerm 知识图谱术语归一化(实体名与关系谓词共用,别名表由调用方传入)。
|
||||
// 幂等:NormalizeKgTerm(NormalizeKgTerm(x)) == NormalizeKgTerm(x)。
|
||||
// 规则保守,只做高置信合并,不做模糊相似度合并,防误合并("人民法院"与"最高人民法院"绝不合并)。
|
||||
// 返回空串表示归一化后无意义(调用方应丢弃);原本合法但归一化后为空时保留原名(如"中华人民共和国")。
|
||||
func NormalizeKgTerm(name string, aliasMap map[string]string) string {
|
||||
orig := name
|
||||
s := strings.TrimSpace(name)
|
||||
s = fullToHalf(s)
|
||||
s = strings.Join(strings.Fields(s), " ")
|
||||
s = strings.Trim(s, "《》")
|
||||
// 去版本注记括号及内容:内容含 年/修正/修订/施行/数字 才剥(人名限定语"张三(北京分公司)"不剥)
|
||||
s = stripVersionParens(s)
|
||||
s = strings.TrimRight(s, "。,;、!?,;!?.·。、")
|
||||
s = strings.TrimSpace(s)
|
||||
// 去"中华人民共和国"前缀(仅前缀;去完为空则保留原名,防实体"中华人民共和国"被删)
|
||||
if strings.HasPrefix(s, "中华人民共和国") {
|
||||
if rest := strings.TrimSpace(strings.TrimPrefix(s, "中华人民共和国")); rest != "" {
|
||||
s = rest
|
||||
}
|
||||
}
|
||||
// 别名全串精确映射(非 contains 替换,防"民诉法"吃掉"民诉法解释")
|
||||
if std, ok := aliasMap[s]; ok && std != "" {
|
||||
s = std
|
||||
}
|
||||
if s == "" && orig != "" {
|
||||
return orig
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// fullToHalf 全角转半角(ASCII 可见区 U+FF01~U+FF5E → U+21~U+7E,全角空格 → 半角空格)
|
||||
func fullToHalf(s string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
switch {
|
||||
case r == ' ':
|
||||
return ' '
|
||||
case r >= '!' && r <= '~':
|
||||
return r - '!' + '\x21'
|
||||
}
|
||||
return r
|
||||
}, s)
|
||||
}
|
||||
|
||||
// stripVersionParens 剥去含版本注记的括号及内容。注记判定:内容含 年/修正/修订/施行 或任一数字。
|
||||
func stripVersionParens(s string) string {
|
||||
pairs := [][2]rune{{'(', ')'}, {'(', ')'}, {'【', '】'}, {'[', ']'}, {'〔', '〕'}}
|
||||
var out strings.Builder
|
||||
runes := []rune(s)
|
||||
for i := 0; i < len(runes); {
|
||||
stripped := false
|
||||
for _, p := range pairs {
|
||||
if runes[i] == p[0] {
|
||||
end := findCloseParen(runes, i, p[1])
|
||||
if end > i && isVersionNote(runes[i+1:end]) {
|
||||
i = end + 1
|
||||
stripped = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !stripped {
|
||||
out.WriteRune(runes[i])
|
||||
i++
|
||||
}
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func findCloseParen(runes []rune, start int, close rune) int {
|
||||
for j := start + 1; j < len(runes); j++ {
|
||||
if runes[j] == close {
|
||||
return j
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// isVersionNote 括号内容是否版本注记(年/修正/修订/施行 或数字)
|
||||
func isVersionNote(content []rune) bool {
|
||||
for _, r := range content {
|
||||
if unicode.IsDigit(r) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
text := string(content)
|
||||
for _, kw := range []string{"年", "修正", "修订", "施行"} {
|
||||
if strings.Contains(text, kw) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user