一个自定义字典工具的源码分析

admin 2023年7月10日01:40:33评论9 views字数 4345阅读14分29秒阅读模式
一个自定义字典工具的源码分析

一个自定义字典工具的源码分析

removeSlashPrefix

func removeSlashPrefix(text string) string {
 for {
  if strings.HasPrefix(text, "/") {
   _, i := utf8.DecodeRuneInString(text)
   text = text[i:]
   continue
  }
  break
 }
 return text
}

这个函数的名字是removeSlashPrefix,它接受一个字符串参数text,返回一个字符串。函数的主要作用是移除输入字符串text的前缀中的所有斜线("/")。

让我们一步一步来解析这个函数:

for { ... }:这是一个无限循环,只有在满足某个条件时才会跳出。

if strings.HasPrefix(text, "/") { ... }:这个if语句检查字符串text是否以斜线("/")开始。如果是,那么就执行花括号内的代码。

_, i := utf8.DecodeRuneInString(text):这行代码会解码字符串text的第一个字符(在这个函数中,这个字符肯定是一个斜线),并返回该字符的长度i。因为Go语言中的字符串是UTF-8编码的,所以一个字符可能占用多个字节。

text = text[i:]:这行代码会移除字符串text的第一个字符,也就是斜线。

continue:这个关键字会使得循环立即跳到下一次迭代,也就是再次检查字符串text的前缀是否还有斜线。

break:这个关键字会使得循环立即结束,也就是当字符串text的前缀不再有斜线时,函数就会返回处理后的字符串。

举个例子,如果我们调用removeSlashPrefix("///hello"),那么函数会返回"hello",因为它移除了字符串前缀的所有斜线。

printIfUnique

func printIfUnique(text string, list map[string]struct{}) {
 text = removeSlashPrefix(text)
 if _, ok := list[text]; !ok {
  fmt.Println(text)
  list[text] = struct{}{}
 }
}

这个函数的名字是printIfUnique,它接受两个参数:一个字符串text和一个映射list。这个映射的键是字符串,值是空结构体。函数的主要作用是检查字符串text是否在映射list中已经存在。如果不存在,那么就打印这个字符串,并将其添加到映射中。

我们一步一步来解析这个函数:

text = removeSlashPrefix(text):这行代码调用了我们之前解析过的函数removeSlashPrefix,移除字符串text的前缀中的所有斜线。

if _, ok := list[text]; !ok { ... }:这个if语句检查字符串text是否在映射list中已经存在。如果不存在(也就是ok为false),那么就执行花括号内的代码。

fmt.Println(text):这行代码会打印字符串text。

list[text] = struct{}{}:这行代码会将字符串text添加到映射list中,值是一个空结构体。

举个例子,如果我们有一个映射list,它的键是字符串,值是空结构体,然后我们调用printIfUnique("///hello", list),那么函数会打印"hello",并将其添加到映射中。如果我们再次调用printIfUnique("///hello", list),那么函数什么也不会做,因为"hello"已经在映射中了。

splitBy

func splitBy(text string, split string, list map[string]struct{}) {
 splitString := strings.Split(text, split)
 for _, match := range splitString {
  if match != "" {
   printIfUnique(match, list)
  }
 }
}

这个函数的名字是splitBy,它接受三个参数:两个字符串text和split,以及一个映射list。这个映射的键是字符串,值是空结构体。函数的主要作用是将字符串text按照字符串split进行分割,然后对分割得到的每个子字符串进行处理。

我们一步一步来解析这个函数:

splitString := strings.Split(text, split):这行代码调用了strings.Split函数,将字符串text按照字符串split进行分割,得到一个字符串切片splitString。

for _, match := range splitString { ... }:这个for循环遍历splitString中的每个元素match。

if match != "" { ... }:这个if语句检查match是否为空字符串。如果不是空字符串,那么就执行花括号内的代码。

printIfUnique(match, list):这行代码调用了我们之前解析过的函数printIfUnique,对字符串match进行处理。

举个例子,如果我们有一个映射list,它的键是字符串,值是空结构体,然后我们调用splitBy("hello/world", "/", list),那么函数会打印"hello"和"world",并将它们添加到映射中。如果我们再次调用splitBy("hello/world", "/", list),那么函数什么也不会做,因为"hello"和"world"已经在映射中了

process

func process(text string, list map[string]struct{}, r *regexp.Regexp) {
 for _, match := range r.FindAllString(text, -1) {
  if match != "" {
   if strings.Contains(match, ".") {
    splitBy(match, ".", list)
   }
   if strings.Contains(match, "/") {
    splitBy(match, "/", list)
   }
   printIfUnique(match, list)
  }
 }
}

这个函数叫做process,它接受三个参数:一个字符串text,一个映射list,以及一个正则表达式对象r。这个映射的键是字符串,值是空结构体。函数的主要作用是处理字符串text,找出所有匹配正则表达式r的子字符串,并对这些子字符串进行进一步的处理。

我们一步一步来解析这个函数:

for _, match := range r.FindAllString(text, -1) { ... }:这个for循环遍历所有匹配正则表达式r的子字符串match。FindAllString函数的第二个参数是-1,表示找出所有的匹配项。

if match != "" { ... }:这个if语句检查match是否为空字符串。如果不是空字符串,那么就执行花括号内的代码。

if strings.Contains(match, ".") { ... }if strings.Contains(match, "/") { ... }:这两个if语句检查match是否包含./。如果包含,那么就调用splitBy函数,将match按照./进行分割,并对分割得到的每个子字符串进行处理。

printIfUnique(match, list):这行代码调用了我们之前解析过的函数printIfUnique,对字符串match进行处理。

举个例子,假设我们有一个映射list,它的键是字符串,值是空结构体,然后我们调用process("hello.world/foo", list, regexp.MustCompile("w+")),那么函数会打印"hello"、"world"、"foo",并将它们添加到映射中。如果我们再次调用process("hello.world/foo", list, regexp.MustCompile("\w+")),那么函数什么也不会做,因为"hello"、"world"和"foo"已经在映射中了。

main

func main() {
 list := make(map[string]struct{}) // store the output lines to check for dupes
 s := bufio.NewScanner(os.Stdin)
 r := regexp.MustCompile(`[a-zA-Z0-9.-_/]*`)
 for s.Scan() {
  process(s.Text(), list, r)
 }
 if s.Err() != nil {
  log.Printf("Error: %sn", s.Err())
 }
}

它的主要作用是从标准输入读取文本,然后对每一行文本进行处理。

我们一步一步来解析这段代码:

list := make(map[string]struct{}):这行代码创建了一个映射list,它的键是字符串,值是空结构体。这个映射用来存储输出的行,以检查是否有重复的行。

s := bufio.NewScanner(os.Stdin):这行代码创建了一个新的扫描器s,用来从标准输入读取文本。

r := regexp.MustCompile([a-zA-Z0-9.-_/]*):这行代码创建了一个新的正则表达式对象r,用来匹配由字母、数字、.、-、_、/组成的字符串。

for s.Scan() { ... }:这个for循环遍历标准输入的每一行。Scan方法读取下一行,并将其作为字符串返回。如果读取成功,Scan方法返回true,否则返回false。

process(s.Text(), list, r):这行代码调用了我们之前解析过的函数process,对每一行文本进行处理。

if s.Err() != nil { ... }:这个if语句检查是否在读取标准输入时发生了错误。如果发生了错误,那么就打印错误信息。

举个例子,假设我们的标准输入是这样的文本:

hello.world/foo
hello.world/bar

那么这段代码会打印:

hello
world
foo
bar

并且,如果我们再次输入hello.world/foo,那么代码不会打印任何东西,因为hello、world和foo已经在映射中了。

福利视频

笔者自己录制的一套php视频教程(适合0基础的),感兴趣的童鞋可以看看,基础视频总共约200多集,目前已经录制完毕,后续还有更多视频出品

https://space.bilibili.com/177546377/channel/seriesdetail?sid=2949374

技术交流

技术交流请加笔者微信:richardo1o1

原文始发于微信公众号(迪哥讲事):一个自定义字典工具的源码分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年7月10日01:40:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   一个自定义字典工具的源码分析https://cn-sec.com/archives/1863799.html

发表评论

匿名网友 填写信息