首先介紹下freestyle外掛:這是一個(gè)根據(jù)韻腳,以詞搜詞的軟件。
看著一堆韻腳一致的詞 + 一點(diǎn)自己的想象力 = 讓你的freestyle開啟了外掛
用下面一個(gè)例子來說明使用場景

說簡單點(diǎn)兒就是在我軟件里搜“老娘舅”,你搜到的一堆詞里有“要講究”。
我寫了一個(gè)微信小程序,總共兩個(gè)頁面,看圖



微信小程序?qū)崿F(xiàn)沒太多的技術(shù)點(diǎn),就倆靜態(tài)頁面 和 一個(gè)接口請(qǐng)求,暫不贅述,如果你感興趣想看前端源碼的話,在評(píng)論里留下郵箱,我會(huì)一一發(fā)給你們的。
恩,前端是這個(gè)樣子,那么我們來看后臺(tái),就是標(biāo)題所謂的“不到20行代碼寫一個(gè)freestyle外掛后臺(tái)”。后臺(tái)用了一個(gè)node.js框架koa,代碼非常的簡潔,支持async/await語法特性,整個(gè)項(xiàng)目除了配置文件,源碼就這一個(gè)不到20行代碼的文件,先看代碼

恩 ,乍一看的確加上注釋,算起來代碼也沒到20行,但感覺還是被作者標(biāo)題黨了。
這感覺就像是去電信局拉了條100兆寬帶,實(shí)際下載速度卻只有10M/秒左右。【括號(hào)】(1B = 8b ,100Mbps = 12.5MB/秒)。的確,號(hào)稱不到20行代碼的freestyle外掛后臺(tái)代碼里也有這么一個(gè)【括號(hào)】,也就是代碼里的第四行,接下去就讓我們來解開這個(gè)括號(hào)。
如各位在第四行所見,韻腳搜詞邏輯實(shí)現(xiàn)部分被我封裝成了一個(gè) 插件“free-style-plugin” ,整個(gè)插件只向外暴露一個(gè)函數(shù) getWords(keyWord,searchCondition) ,我們只需向其傳遞兩個(gè)參數(shù):搜索關(guān)鍵詞,搜索類型(全壓,單壓,英文),它就會(huì)向我們返回在詞庫中搜索到的結(jié)果。
首先,我整體地介紹一下我實(shí)現(xiàn)韻腳搜詞這個(gè)功能的主要步驟
- 初始化詞庫,包括中文詞庫,英文詞庫。(把網(wǎng)上找的詞庫初始化成我自己定義的數(shù)據(jù)格式)
- 根據(jù)搜索條件,提取搜索關(guān)鍵詞的韻腳。(中文搜索用的是韻母搜索,英文搜索用的是聲母搜索)
- 根據(jù)搜索條件,在相應(yīng)詞庫中搜索韻腳匹配的單詞并返回
我調(diào)研后采用的幾個(gè)第三方庫
- 一個(gè)中文詞庫: ling0322/webdict
- 漢語拼音轉(zhuǎn)換庫: hotoo/pinyin
- 英文發(fā)音庫(提取英文單詞的發(fā)音): The CMU Pronouncing Dictionary
那就從先進(jìn)步,初始化詞庫講起
/** * 初始化中文詞庫 */ function initDictYunMu() { var promise = new Promise( (resolve, reject) => { fs.readFile(__dirname + '/data/dict.txt', 'utf-8', function(err, data) { if (err) { console.error(err); } else { var start = new Date().getTime(); /** * txt中的詞庫轉(zhuǎn)換成一個(gè)數(shù)組 */ var arrRaw = data.split('\n'); /** * [arr 數(shù)據(jù)清洗之后的詞庫] */ var arr = []; arrRaw.forEach(item => { var itemSplit = item.split(' ') /** * [構(gòu)建一個(gè)新的對(duì)象push進(jìn)數(shù)組] * @name 單詞 * @weight 使用頻率 * @yunMu 韻母 */ arr.push({ name: itemSplit[0], weight: itemSplit[1], yunMu: getYunMu(itemSplit[0]) }); }) /** * 根據(jù)單詞的 使用頻率 & 單詞的長短 排序 * 去除單個(gè)字的結(jié)果 ,用來單壓搜索 */ arr = arr.sort((a, b) => (a.weight - b.weight)).sort((a, b) => (a.name.length - b.name.length)).filter(value => { return value.name.length > 1 }) var end = new Date().getTime(); // console.log(`處理詞庫220626多詞共花費(fèi)${(end-start)/1000}秒`); // console.log(arr); dict = arr; resolve() } }); }) return promise }
用fs.readFile讀取txt詞庫文件,根據(jù)分隔符把數(shù)據(jù)轉(zhuǎn)換成數(shù)組,遍歷數(shù)組把數(shù)組內(nèi)容轉(zhuǎn)換成我們想要的格式,比如 {name:'外掛',weight: 3, yunMu: ',ai,ua'},再根據(jù)詞語的使用頻率和單詞長短排序。 初始化中文詞庫(220626個(gè)詞)大概要花7秒鐘左右 。
其實(shí)剛開始的時(shí)候,(因?yàn)樯厦嫣岬降某跏蓟~庫是異步的,大概需要7秒時(shí)間),npm插件暴露了兩個(gè)函數(shù): init() , getWords() 。在后臺(tái)啟動(dòng)的時(shí)候先執(zhí)行 init() ,攔截到路由 /findSameRhymeWords 就執(zhí)行 getWords()。 但后來發(fā)現(xiàn)這樣實(shí)現(xiàn)有弊端:1.增加插件的使用成本。2.在后臺(tái)詞庫未初始化完成的時(shí)候請(qǐng)求會(huì)報(bào)錯(cuò)。
所以代碼進(jìn)行如下改造
- 初始化的時(shí)候把這個(gè)值緩存下來(也就是代碼中的全局變量 dict),之后每次請(qǐng)求進(jìn)來如果緩存有值就在這個(gè)變量里搜索就好了,沒值就初始化詞庫。
- 這里我用了 promise 的寫法,讓初始化中文詞庫,初始化英文詞庫,搜詞以比較簡潔的形式 同步執(zhí)行 ,也避免了所謂的回調(diào)地獄。
所以npm最終只暴露一個(gè)函數(shù): getWords()

第二步,提取搜索關(guān)鍵詞的韻腳
這個(gè)步驟主要用到的是 上文提到的 pinyin 庫,這個(gè)庫可根據(jù)輸入中文拿到 中文對(duì)應(yīng)的拼音,聲母,雖然沒有直接的函數(shù)去拿關(guān)鍵詞的韻母,但通過(全拼 - 聲母)我們可以自己得出關(guān)鍵詞的韻母。

第三步,搜索韻腳匹配的單詞
中文搜詞,遍歷整個(gè)緩存的詞庫 ,用indexOf去匹配詞,并判斷單詞末端匹配。

英文搜詞,遍歷整個(gè)詞庫后,我這邊采用正則去匹配,正則可能需要用一個(gè)例子解釋下:比如說我搜索一個(gè)中文 “白頭發(fā)” ,然后我取到 聲母 “btf”,形成正則“B.*T.*F”,然后去英文詞庫匹配 發(fā)音類似的詞,比如就能匹配到結(jié)果:“ beautiful”。(目前這個(gè)功能正處于beta階段,搜索方式應(yīng)該還得優(yōu)化)

軟件的細(xì)節(jié)方面介紹完了,已發(fā)布至npm,歡迎各位安裝使用
$ npm install free-style-plugin
你也可以參照上面【圖四】后臺(tái)的寫法用不到二十行的代碼搭建一個(gè)韻腳搜詞后臺(tái),當(dāng)然如果你對(duì)插件源碼感興趣,可以訪問 free-style-plugin源碼git地址 : nigulasikk/freeStylePlugin ,歡迎各位star,fork,指教。
部署中遇到的其他問題
- node.js環(huán)境。先更新node.js至比較新的版本,如(v8.2.1)
- 服務(wù)器gcc版本。本地調(diào)試都好好的,上服務(wù)器發(fā)現(xiàn)不行,最后鎖定錯(cuò)誤發(fā)現(xiàn)需要用pinyin庫里依賴的jieba詞庫這邊需要高的gcc版本
- https證書。因?yàn)槲⑿判〕绦蚍?wù)端必須是https的,所以得升級(jí)證書。 Let's Encrypt 是一個(gè)比較有名的免費(fèi)證書,里面找了一個(gè)能自動(dòng)升級(jí)centos系統(tǒng)對(duì)應(yīng)證書并注入nginx配置的傻瓜式方案: Certbot 。
最后
夏天《中國有嘻哈》很火的時(shí)候,我就跟身邊的人吹牛逼說我要寫這么一個(gè)軟件,但一直沒好好寫?,F(xiàn)在快半年過去了,正好有點(diǎn)兒時(shí)間,就不讓吹過的牛逼隨青春一笑了之了。
網(wǎng)上看到一句蠻有道理的話,我記了下來: “不要為了押韻而失去歌詞的意義” 。
但我想說: “開啟freestyle外掛后,punchline中歌詞意義和押韻是可以完美融合的”。
最后的最后
我是不懂音樂的,就先做了這么個(gè)玩具, 如果有這方面研究很先進(jìn)的朋友,歡迎聯(lián)系我qiankaijie1024@gmail.com,提些建議,我提供免費(fèi)改代碼服務(wù)哈!