讓程序自動編寫程序
最近利用 gcin 的倉頡對照表寫了一個小程序。( 簡單來說就是一個 CLI 的倉頡轉換器 )
為什麼要做這個東西呢?當然這是為了可以在公司寫 blog 而不被人發現啊!想想如果明目張膽地於眾目睽睽之下打那麼多中文字,這很快就會被發現吧!
嘛,愉懶的事情先到這裏,今天想討論一下能將對照表編譯為源碼的技術。
對照表轉換的我在制作 wenku8 app 的時候,其中一環需為港台網民制作 簡=>繁 轉換1。
由於簡繁不能直接對照2,於是我找到了新同文堂的對照表,然後花了整整兩天時間將編譯方法苦了出來。
記得當時是基於效能問題得利用 C ++ 編寫這個程序,因為每篇文章可能有幾萬字,而每個中文字則有 4~6 個字節,轉換列表又有一萬多行,再加上轉換量,用 replace 實在不怎麼說得通。
於是我就寫起來了,在網上問到了「最快3」的轉換方法4後,剛剛開始寫的時候是這麼做的:
if( strcmp(str, "0海里") == 0 )
{
out = "0海里";
step_size = 8;
}
else if ( strcmp(str, "0天后") == 0 )
{
out = "0天後";
step_size = 8;
}
...於是我想了個巢狀判定 ( nested if ) 的方法:
if( str[start + 0] == 239 && str[start + 1] == 188 )
{
if( str[start + 2] == 144 )
{
if( str[start + 3] == 232 && str[start + 4] == 163 && str[start + 5] == 161 )
{
out = "0里";
step_size = 5;
}
else if ( str[start + 3] == 230 && str[start + 4] == 181 && str[start + 5] == 183 )
{
if( str[start + 6] == 232 && str[start + 7] == 163 && str[start + 8] == 161 )
{
out = "0海里";
step_size = 8;
}
if( step_size == 0 )
{
out = "0海";
step_size = 5;
}
}
else if ( str[start + 3] == 228 && str[start + 4] == 189 && str[start + 5] == 153 )
{
out = "0餘";
step_size = 5;
}
}
...這個方法可以稱為最少判定量了,每個判定都不會在下一個判定中重複5。
但惡夢就在這裏開始了—我要怎麼讓程序知道什麼時候要包在裏面,什麼時候要 return 呢?
當腦袋要快要超載的時候,我將想法畫了出來:
表示式為: -> RGMMMO GMM RGMMM G ->
R: 紅 ( red )
G: 綠 ( green )
M: 洋紅 ( magenta )
O: 橙 ( orange )
在說明之前,先得了解中文字是屬於多元 ( multi-byte ) 字碼,畢竟要將一萬多個中文字以兩個 F6來表示怎麼想都不太可能。
而上面的數字就代表每個 byte 的值。在 UTF-8 上多數中文字大概位於三個字元的範圍7。
由於每個 F 只會判定一次,因此要將不同的 byte 的值分組:
// 以下三個字組有相同的F值(239),因此239為一個判定。
// 下面在細字子字組,如此類推。
// 239 240 221
// 239 241
// 239 250
if( 239 )
{
if ( 240 && 221 )
{
// output
}
else if( 241 )
{
...
}
else if( 250 )
.
.
.
}
...分組亦有其巧妙之處,詞組表中有很多這樣的情況:
不干, 不干
不干不淨, 不乾不淨
不干他, 不干他
不干休, 不干休
不干你, 不干你if( 不干 )
{
if ( 不淨 )
{
// 不乾不淨
step_size = 8;
}
else if ( 他 )
{
// 不干他
step_size = 6;
}
...
// 這裏判定如沒有接續
if ( step_size = 0 )
{
// 不干
}
}因此分組的排列算法必須準確!
概念想好了,接下來是實作,沒想到這居然是最難的地方啊:
初期源碼 ( php ),( 後期版本已改為 Class ( ByteMap ),將往後發佈 ):
寫好之後做了幾次測試,效果拔群。
可惜文庫因為安全性方面的考量而打消了這個方法,這樣就過了半年……
就在我整理這些「雜物」時找到了這個程序,亦正在煩惱著沒什麼時間寫 blog ,該如何在公司寫而不讓人發現的方法時。突然靈機一觸「倉頡碼也是對照表吧!」,便動手將它寫成用途更廣範的 Class ( ByteMap ) 。花了兩天時間給做出來了!
- 其實最後文庫方拾棄了這個辦法,最後改用php自帶的strtr來做轉換了(這個方法在php 5.5之後的確比較快。於是這兩天苦出來的東西卻變成無用功了)。
- 簡體可以說是繁體的「有損壓縮」,繁體的「發」、「髮」相等於簡體的「发」。單一個「发」並不能只轉換成「發」或者「髮」。就像mp3一樣,某些資息被刪掉後無法回頭了。
- 其實還有另一種方法, PHP 的 strtr 是以存取記憶體住址作轉換的。
- 其實就是最蠢的方法:每個字詞都以IF作判段輸出,在編譯為機器碼時編譯器就會懂得如何「最佳化」。
- 像詞組「發光」、「發熱」,之前的方法會在「發」這個字重被做判段,效能上不可取。
- 就是指 0xFF ( 1 byte )
- 新同文堂的列表是有分「字列表」跟「詞組列表」的,因此 byte 的長度可多於三個 byte 。
- 我把這個叫 做 whole-byte sequence
Thu Feb 06 2014 16:14:02 GMT+0000 (Coordinated Universal Time)
Last modified: Fri Mar 11 2016 14:32:53 GMT+0000 (Coordinated Universal Time)
Comments
No comments here.
Do you even comment?
website:
Not a valid website
Invalid email format
Please enter your email
*Name:
Please enter a name
Submit
抱歉,Google Recaptcha 服務被牆掉了,所以不能回覆了