5.4 Go语言项目实战:基于开源数据的成语查询

Amiee7 · · 424 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。
本篇文章IT兄弟连GO语言学院小美 给读者们分享一下Go语言项目实战:基于开源数据的成语查询 对GO语言感兴趣想要学习Golang开发技术的小伙伴就随小编来了解一下吧。 **业务需求** - 命令行键入一行诗句启动应用:idiom.exe -cmd start -poem 大王派我来巡山 - 将诗句中的每个字丢入【模糊查询管道】 - 另外再建立【精确查询管道】和【结束管道】,分别存储【成语】(大鹏展翅、占山为王、龟派气功...)和【结束指令】(fuckoff) - 时钟每秒随机读入一条管道数据: 如果是【模糊查询管道】:起协程进行模糊查询,并汇总数据在内存 如果是【精确查询管道】:起协程进行精确查询,并汇总数据在内存 如果是【结束指令】:停止查询,将内存中的数据持久化为json并退出; **命令行参数获取工具** ``` import ( "fmt" "flag" ) /* argInfos 要获取的命令行参数们 例如:[3]interface{}{"cmd","默认命令","要执行的命令"} retValuesMap 以map的形式返回用户在命令行输入的值 [cmd:getmoney amount:666 to:张三] */ func GetCmdlineArgs(argInfos ...[3]interface{}) (retValuesMap map[string]interface{}) { fmt.Printf("type=%T,value=%v\n", argInfos, argInfos) //初始化返回结果 retValuesMap = map[string]interface{}{} //预定义【用户可能输入的各种类型的指针】 var strValuePtr *string var intValuePtr *int //预定义【用户可能输入的各种类型的指针】的容器 //用户可能输入好几个string型的参数值,存放在好几个string型的指针中,将这些同种类型的指针放在同种类型的map中 //例如:flag.Parse()了以后,可以根据【strValuePtrsMap["cmd"]】拿到【存放"cmd"值的指针】 var strValuePtrsMap = map[string]*string{} var intValuePtrsMap = map[string]*int{} /* var floatValuePtr *float32 var floatValuePtrsMap []*float32 var boolValuePtr *bool var boolValuePtrsMap []*bool*/ //遍历用户需要接受的所有命令定义 for _, argArray := range argInfos { /* 先把每个命令的名称和用法拿出来, 这俩货都是string类型的,所有都可以通过argArray[i].(string)轻松愉快地获得其字符串 一个叫“cmd”,一个叫“你想干嘛” "cmd"一会会用作map的key */ //[3]interface{} //["cmd" "未知类型" "你想干嘛"] //["gid" 0 "要查询的商品ID"] //上面的破玩意类型[string 可能是任意类型 string] nameValue := argArray[0].(string) //拿到第一个元素的string值,是命令的name usageValue := argArray[2].(string) //拿到最后一个元素的string值,是命令的usage //判断argArray[1]的具体类型 switch argArray[1].(type) { case string: //得到【存放cmd的指针】,cmd的值将在flag.Parse()以后才会有 //cmdValuePtr = flag.String("cmd", argArray[1].(string), "你想干嘛") strValuePtr = flag.String(nameValue, argArray[1].(string), usageValue) //将这个破指针以"cmd"为键,存在【专门放置string型指针的map,即strValuePtrsMap】中 strValuePtrsMap[nameValue] = strValuePtr case int: //得到【存放gid的指针】,gid的值将在flag.Parse()以后才会有 //gidValuePtr = flag.String("gid", argArray[1].(int), "商品ID") intValuePtr = flag.Int(nameValue, argArray[1].(int), usageValue) //将这个破指针以"gid"为键,存在【专门放置int型指针的map,即intValuePtrsMap】中 intValuePtrsMap[nameValue] = intValuePtr } } /* 程序运行到这里,所有不同类型的【存值指针】都放在对相应类型的map中了 flag.Parse()了以后,可以从map中以参数名字获取出【存值指针】,进而获得【用户输入的值】 */ //用户输入完了,解析,【用户输入的值】全都放在对应的【存值指针】中 flag.Parse() /* 遍历各种可能类型的【存值指针的map】 */ if len(strValuePtrsMap) > 0 { //从【cmd存值指针的map】中拿取cmd的值,还以cmd为键存入结果map中 for k, vPtr := range strValuePtrsMap { retValuesMap[k] = *vPtr } } if len(intValuePtrsMap) > 0 { //从【gid存值指针的map】中拿取gid的值,还以gid为键存入结果map中 for k, vPtr := range intValuePtrsMap { retValuesMap[k] = *vPtr } } //返回结果map return } ``` **定义成语数据模型** ``` import "fmt" //成语 type Idiom struct { Title string Spell string Content string Sample string Derivation string } //打印成语数据 func PrintIdiom(idiom Idiom) { if idiom.Title != "" { fmt.Printf("Title:%s\n", idiom.Title) fmt.Printf("Spell:%s\n", idiom.Spell) fmt.Printf("Sample:%s\n", idiom.Sample) fmt.Printf("Derivation:%s\n", idiom.Derivation) fmt.Printf("Content:%s\n", idiom.Content) } else { fmt.Println("未找到成语!") } } ``` **定义成语数据模型和JSON的互化工具** ``` import ( "encoding/json" "os" "fmt" ) //将模糊查询的json转化为go数据 func ParseJson2Idioms(jsonStr string) (idiomsMap map[string]Idiom) { idiomsMap = make(map[string]Idiom) //将json转换为go数据 tempMap := make(map[string]interface{}) json.Unmarshal([]byte(jsonStr), &tempMap) //fmt.Println(tempMap) dataSlice := tempMap["showapi_res_body"].(map[string]interface{})["data"].([]interface{}) //fmt.Printf("type=%T,value=%v",dataSlice,dataSlice) for _, v := range dataSlice { title := v.(map[string]interface{})["title"].(string) idiom := Idiom{Title: title} idiomsMap[title] = idiom } return } //将精确查询的json转化为go数据 func ParseJson2Idiom(jsonStr string) Idiom { idiom := Idiom{} tempMap := make(map[string]interface{}) json.Unmarshal([]byte(jsonStr), &tempMap) dataMap := tempMap["showapi_res_body"].(map[string]interface{})["data"].(map[string]interface{}) for k, v := range dataMap { valueStr := v.(string) switch k { case "title": idiom.Title = valueStr case "spell": idiom.Spell = valueStr case "samples": idiom.Sample = valueStr case "derivation": idiom.Derivation = valueStr case "content": idiom.Content = valueStr } } return idiom } //将go数据写出到json文件 func WriteIdioms2File(idiomsMap map[string]Idiom, path string) { fmt.Println("WriteIdioms2File") dstFile, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) defer dstFile.Close() encoder := json.NewEncoder(dstFile) err := encoder.Encode(idiomsMap) if err != nil { fmt.Println("写出json文件失败,err=", err) return } fmt.Println("写出json文件成功!") } //读入json文件为go数据 func ReadIdiomsFromFile(dstPath string) (idiomsMap map[string]Idiom, err error) { idiomsMap = make(map[string]Idiom) //读入json文件数据 dstFile, _ := os.OpenFile(dstPath, os.O_RDONLY|os.O_CREATE, 0666) defer dstFile.Close() decoder := json.NewDecoder(dstFile) err = decoder.Decode(&idiomsMap) if err != nil { fmt.Println("加载数据失败!err=", err) } else { fmt.Println("成功加载数据!") fmt.Println("idiomsMap=", idiomsMap) } return } ``` **定义网络工具** ``` import ( "net/http" "fmt" "io/ioutil" ) //获取模糊查询的url func GetAmbiguousUrl(keyword string,page string)(url string){ return "http://route.showapi.com/1196-1?showapi_appid=19988&showapi_sign=968ad4fcc2144e41b5c366838d1b0ec4&keyword="+keyword+"&page="+page+"&rows=20" } //获取精确查询的url func GetAccurateUrl(keyword string)(url string){ return "http://route.showapi.com/1196-2?showapi_appid=19988&showapi_sign=968ad4fcc2144e41b5c366838d1b0ec4&keyword="+keyword } //从url拿到json数据 func GetJson(url string) (jsonStr string, err error) { //获得网络数据 resp, err := http.Get(url) if err != nil { fmt.Println("http请求失败,err=", err) return } //延时关闭网络IO资源 defer resp.Body.Close() //resp.Body实现了Reader接口,对其进行数据读入 bytes, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("读取网络数据失败,err=", err) return } //将网络数据转化为字符串输出 jsonStr = string(bytes) //fmt.Println(jsonStr) return } //模糊查询 func DoAmbiguousQuery(keyword string,page string,chanAccurate chan<- string) { //先拿到json url := GetAmbiguousUrl(keyword, page) jsonStr, _ := GetJson(url) //将json转化为成语集合 idiomsMap := ParseJson2Idioms(jsonStr) //将成语集合写入内存数据 for title,idiom := range idiomsMap{ dbDataMap[title] = idiom } //将成语的名字写入精确管道 for title,_ := range idiomsMap{ chanAccurate<- title } /* chanAccurate<- "大鹏展翅" chanAccurate<- "隔壁老王" chanAccurate<- "龟派气功" chanAccurate<- "我很牛逼" chanAccurate<- "来咬我呀" fmt.Println("DoAmbiguousQuery",keyword,page) */ } //精确查询 func DoAccurateQuery(keyword string) { //fmt.Println("DoAccurateQuery",keyword) //拿到json url := GetAccurateUrl(keyword) jsonStr, _ := GetJson(url) //将json转化为一个Idiom对象 idiom := ParseJson2Idiom(jsonStr) //将Idiom对象存入总集合,覆盖原来的粗糙对象 dbDataMap[idiom.Title] = idiom } ``` **主调模块** ``` import ( "fmt" "time" "os" ) const DB_PATH = "d:/temp/idioms-v2.0.json" var ( //数据管道 chanAmbiguous = make(chan string, 20) chanAccurate = make(chan string, 20) chanQuit = make(chan string, 0) //全局内存数据 dbDataMap = make(map[string]Idiom) ) func main0() { //读入命令行参数 //idiom.exe -cmd start -poem 大王派我来巡山 cmdInfo := [3]interface{}{"cmd","未知命令","你打算干什么"} poemInfo := [3]interface{}{"poem","绞尽果汁想不出","用于启动的一行诗句"} retValuesMap := GetCmdlineArgs(cmdInfo,poemInfo) cmd := retValuesMap["cmd"].(string) poem := retValuesMap["poem"].(string) fmt.Println(cmd, poem) //将读入的诗句打碎丢入模糊管道 for _, v := range poem { keyword := fmt.Sprintf("%c", v) chanAmbiguous <- keyword } //三选一读入管道数据,周期性执行 go func() { ticker := time.NewTicker(time.Second) for { <-ticker.C select { case keyword := <-chanAmbiguous: go DoAmbiguousQuery(keyword,"1",chanAccurate) case keyword := <-chanAccurate: go DoAccurateQuery(keyword) case <-chanQuit: WriteIdioms2File(dbDataMap, DB_PATH) os.Exit(0) } } }() //定时20秒结束主程序 timer := time.NewTimer(20 * time.Second) <-timer.C chanQuit <- "OVER" } ``` 想要了解更多关于GO语言开发方面内容的小伙伴, 请关注IT兄弟连官网、公众号:GO语言研习社, IT兄弟连教育有专业的微软、谷歌讲师为您指导, 此外IT兄弟连老师精心推出的GO语言教程定能让你快速掌握GO语言从入门到精通开发实战技能。

入群交流(和以上内容无关):Go中文网 QQ 交流群:798786647 或加微信入微信群:274768166 备注:入群;关注公众号:Go语言中文网

424 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传