Go語言專案實戰:基於開源資料的成語查詢

尹成發表於2018-11-16

業務需求

  • 命令列鍵入一行詩句啟動應用: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語言視訊主頁
https://edu.csdn.net/lecturer/1928

[清華團隊帶你實戰區塊鏈開發]

(https://ke.qq.com/course/344443?tuin=3d17195d)
掃碼獲取海量視訊及原始碼 QQ群:721929980
在這裡插入圖片描述

相關文章