nailcui/Nailcui.github.io

代码备份

Opened this issue · 0 comments

redis keys匹配的golang实现


// Glob-style pattern matching.
func StringMatch(pattern, str string) bool {
	if pattern == "*" {
		return true
	}
	patternLen := len(pattern)
	strLen := len(str)
	for patternLen > 0 {
		switch pattern[0] {
		case '*':
			for patternLen > 1 && pattern[1] == '*' {
				pattern = pattern[1:]
				patternLen--
			}
			if patternLen == 1 {
				return true
			}
			for strLen > 0 {
				if StringMatch(pattern[1:], str) {
					return true
				}
				str = str[1:]
				strLen--
			}
			return false
		case '?':
			if strLen == 0 {
				return false
			}
			str = str[1:]
			strLen--
		case '[':
			var not, match bool
			var prePattern string
			pattern = pattern[1:]
			patternLen--
			not = patternLen > 0 && pattern[0] == '^'
			if not {
				prePattern = pattern
				pattern = pattern[1:]
				patternLen--
			}
			match = false
			for {
				if patternLen > 0 && pattern[0] == '\\' {
					pattern = pattern[1:]
					patternLen--
					match = pattern[0] == str[0]
				} else if patternLen > 0 && pattern[0] == ']' {
					break
				} else if patternLen == 0 {
					pattern = prePattern
					patternLen = len(pattern)
					break
				} else if patternLen >= 3 && pattern[1] == '-' {
					start := pattern[0]
					end := pattern[2]
					c := str[0]
					if start > end {
						start, end = end, start
					}
					pattern = pattern[2:]
					patternLen-=2
					if c >= start && c <= end {
						match = true
					}
				} else if pattern[0] == str[0] {
					match = true
				}
				prePattern = pattern
				pattern = pattern[1:]
				patternLen--
			}
			if not {
				match = !match
			}
			if !match {
				return false
			}
			str = str[1:]
			strLen--
		case '\\':
			if patternLen >= 2 {
				pattern = pattern[1:]
				patternLen--
			}
			if string(pattern[0]) != string(str[0]) {
				return false
			}
			str = str[1:]
			strLen--
		default:
			if string(pattern[0]) != string(str[0]) {
				return false
			}
			str = str[1:]
			strLen--
		}
		pattern = pattern[1:]
		patternLen--
		if strLen == 0 {
			for patternLen > 0 && pattern[0] == '*' {
				pattern = pattern[1:]
				patternLen--
			}
			break
		}
	}
	if patternLen == 0 && strLen == 0 {
		return true
	}
	return false
}

单元测试


func TestStringMatch(t *testing.T) {
	assertions := assert.New(t)
	tests := []struct {
		pattern string
		str     string
		want    bool
	}{
		{"a**", "abcd", true},
		{"a**b", "abcd", false},
		{"abcd", "abcd", true},
		{"*", "abcd", true},
		{"\\a", "a", true},
		{"a?c", "abc", true},
		{"a?c", "abbc", false},
		{"ab*", "ab", true},
		{"ab*", "abcd", true},
		{"ab*cd", "abcd", true},
		{"ab*cd", "ab-cd", true},
		{"ab*cd", "ab--cd", true},
		{"ab[ef]", "abe", true},
		{"ab[ef]", "abf", true},
		{"ab[ef]", "abef", false},
		{"[ef]cd", "ecd", true},
		{"[ef]cd", "fcd", true},
		{"[ef]cd", "efcd", false},
		{"ab[ef]cd", "abecd", true},
		{"ab[ef]cd", "abfcd", true},
		{"ab[ef]cd", "abefcd", false},
		{"ab[^e]cd", "abfcd", true},
		{"ab[^e]cd", "abffcd", false},
		{"ab[^ef]cd", "abfcd", false},
		{"ab[^ef]cd", "abwcd", true},
		{"ab[a-d]cd", "abacd", true},
		{"ab[d-a]cd", "abacd", true},
		{"ab[a-d]cd", "ababcd", false},
		{"ab[a-d]cd", "abecd", false},
		{"ab[a-d]cd", "abefcd", false},
		{"ab[a-d]cd", "ababcd", false},
		{"ab[", "ab[", false},
		{"ab[a", "ab[a", false},
		{"ab[[", "ab[", true},
		{"ab[[a", "ab[", true},
		{"ab[[ab", "ab[", true},
		{"ab[[a", "ab[a", false},
	}
	for _, tt := range tests {
		t.Run(tt.pattern, func(t *testing.T) {
			assertions.Equal(tt.want, StringMatch(tt.pattern, tt.str),
				fmt.Sprintf("TestStringMatch pattern: %s, str: %s", tt.pattern, tt.str))
		})
	}
}