本例中代碼已經(jīng)有詳細注釋故痊,主要體現(xiàn)了Gox語言編碼檢測豫缨、二進制字節(jié)(byte)數(shù)據(jù)類型的處理、使用選擇文件對話框和信息確認對話框摔桦、使用LCL庫編寫GUI圖形界面等知識社付。本例也可以舉一反三用于檢測和轉(zhuǎn)換其他文件編碼。
下面是運行該程序的效果截圖邻耕,可以在文本框中粘貼入所需檢測的文本鸥咖,也可以點下面的“打開文件”按鈕來選擇所需檢測的文件,然后程序會自動檢測該文件的編碼兄世,如果是GB18030的編碼啼辣,會提示用戶確認是否要進行編碼轉(zhuǎn)換,用戶確認后會進行從GB18030至UTF-8的編碼轉(zhuǎn)換碘饼。
運行效果截圖
// quickCheck函數(shù)用于根據(jù)文件頭快速判斷文件的類型
// 輸入?yún)?shù)是一個字節(jié)數(shù)組
// 注意無論是GB18030還是UTF-8編碼的文件熙兔,均可以沒有對應(yīng)的文件頭,
// 因此判斷文件頭并不總能判斷出文件類型
// 但一般來說存在對應(yīng)文件頭艾恼,就可以確定是該編碼的文件
quickCheck = fn(bytesA) {
lenT = len(bytesA)
if lenT < 3 {
return ""
}
// 判斷一開始是否是UTF-8的BOM頭住涉,如果是的話,直接可斷定是
if bytesA[0] == 0xef && bytesA[1] == 0xbb && bytesA[2] == 0xbf {
return "UTF8-BOM"
}
if lenT < 4 {
return ""
}
// 判斷是否是GB18030文件的文件頭
if bytesA[0] == 0x84 && bytesA[1] == 0x31 && bytesA[2] == 0x95 && bytesA[3] == 0x33 {
return "GB18030"
}
return ""
}
// 詳細檢查是否是UTF-8編碼的文件
// 方法是注意檢查每個字符钠绍,看是否存在UTF-8編碼規(guī)則之外的字符
// 如果全是規(guī)則之內(nèi)的字符舆声,則可以判定是UTF-8編碼
checkUTF8 = fn(bytesA) {
utf8ByteT = 0
for i, v = range bytesA {
c = int(v)
if utf8ByteT == 0 {
if c >= 0x00 && c <= 0x7F {
continue
}
if c >= 0xC0 && c <= 0xDF {
utf8ByteT = 1
continue
}
if c >= 0xE0 && c <= 0xEF {
utf8ByteT = 2
continue
}
if c >= 0xF0 && c <= 0xF7 {
utf8ByteT = 3
continue
}
} else {
if c >= 0x80 && c <= 0xBF {
utf8ByteT -= 1
continue
}
}
return false
}
return true
}
// 詳細檢查是否是GB18030編碼的文件
// 方法是注意檢查每個字符,看是否存在GB18030編碼規(guī)則之外的字符
// 如果全是規(guī)則之內(nèi)的字符柳爽,則可以判定是GB18030編碼
checkGB18030 = fn(bytesA) {
gbByteT = 0
for i, v = range bytesA {
c = int(v)
if gbByteT == 0 {
if c >= 0x00 && c <= 0x7F {
continue
}
if c >= 0x81 && c <= 0xFE {
gbByteT = 1
continue
}
} elif gbByteT == 1 {
if (c >= 0x40 && c <= 0x7E) || (c >= 0x80 && c <= 0xFE) {
gbByteT = 0
continue
}
if c >= 0x30 && c <= 0x39 {
gbByteT = 2
continue
}
} elif gbByteT == 2 {
if c >= 0x81 && c <= 0xFE {
gbByteT = 3
continue
}
} else {
if c >= 0x30 && c <= 0x39 {
gbByteT = 0
continue
}
}
return false
}
return true
}
// 詳細檢查文件編碼的入口函數(shù)
// 可以調(diào)節(jié)其中的判斷順序來選擇優(yōu)先判斷何種編碼
// 本例中優(yōu)先先判定是否是UTF-8編碼
// 如果沒有任何字符不屬于UTF8編碼的規(guī)則媳握,則認定為UTF-8編碼
// 接下來才判斷GB18030
// 如果都不符合,則返回空字符串
detailCheck = fn(bytesA) {
if checkUTF8(bytesA) {
return "UTF-8"
}
if checkGB18030(bytesA) {
return "GB18030"
}
return ""
}
// 檢查編碼的入口函數(shù)
// 先用快速檢測方法(檢測文件頭)來試圖檢測文件編碼
// 快速法無法檢測出的情況下磷脯,才進行詳細檢測
checkText = fn(bytesA) {
rs = quickCheck(bytesA)
if rs == "" {
rs = detailCheck(bytesA)
}
return rs
}
// 下面才是主程序部分的開始
// 初始化LCL圖形界面庫
errT = lcl.InitLCL()
if errT != nil {
plerr(errT)
exit()
}
// 創(chuàng)建LCL圖形界面應(yīng)用并初始化
application = lcl.GetApplication()
application.Initialize()
// 設(shè)置應(yīng)用標題
application.SetTitle("編碼檢測與轉(zhuǎn)換")
application.SetMainFormOnTaskBar(true)
// 創(chuàng)建主窗口并設(shè)置寬高蛾找、標題、位置等
mainForm = application.CreateForm()
mainForm.SetWidth(620)
mainForm.SetHeight(480)
mainForm.SetCaption("編碼檢測與轉(zhuǎn)換")
mainForm.SetPosition(lcl.PoScreenCenter)
mainForm.Font().SetSize(11)
// 創(chuàng)建一個多行文本框赵誓,用于放轉(zhuǎn)換后的文本
memo1 = lcl.NewMemo(mainForm)
memo1.SetParent(mainForm)
memo1.SetBounds(10, 20, 600, 400)
memo1.Font().SetSize(11)
memo1.SetAnchors(lcl.NewSet(lcl.AkTop, lcl.AkBottom, lcl.AkLeft, lcl.AkRight))
memo1.SetScrollBars(lcl.SsAutoVertical)
memo1.SetWordWrap(true)
// 設(shè)置按“打開文件”按鈕后的回調(diào)函數(shù)
onButtonOpenClick = fn(sender) {
// 打開圖形化的文件選擇框讓用戶選擇文件
fileNameT = gui.SelectFile("選擇文件……", "所有類型", "*")
if tk.IsErrorString(fileNameT) {
gui.SimpleError("錯誤提示", tk.GetErrorString(fileNameT))
return
}
// 以二進制方式載入文件中的所有字節(jié)
bytesT, errT = tk.LoadBytesFromFileE(fileNameT, -1)
if errT != nil {
gui.SimpleError("錯誤提示", errT.Error())
return
}
// 檢測文本編碼
rs = checkText(bytesT)
if rs == "" {
gui.SimpleError("錯誤提示", "無法推測編碼")
return
}
outStrT = ""
// 如果是GB18030編碼打毛,則提示用戶確認是否轉(zhuǎn)換為UTF-8編碼;否則直接顯示該文件
if rs == "GB18030" {
confirmT = gui.GetConfirm("請選擇", "文件編碼是"+rs+"俩功,是否轉(zhuǎn)換為UTF-8幻枉?")
if confirmT {
outStrT = tk.ConvertToUTF8(bytesT, "GB18030")
} else {
outStrT = string(bytesT)
}
} elif rs == "UTF8-BOM" {
confirmT = gui.GetConfirm("請選擇", "文件編碼是"+rs+",是否去除BOM頭诡蜓?")
if confirmT {
outStrT = string(bytesT[3:])
} else {
outStrT = string(bytesT)
}
} else {
outStrT = string(bytesT)
}
memo1.SetText(outStrT)
}
// 點按“另存為”按鈕的回調(diào)函數(shù)
onButtonSaveAsClick = fn(sender) {
// 提示用戶確認
confirmT = gui.GetConfirm("請選擇", "如果文件沒有轉(zhuǎn)換為UTF-8熬甫,保存時可能會造成字符錯亂并無法恢復(fù),是否要繼續(xù)蔓罚?")
if !confirmT {
return
}
// 彈出另存文件的對話框讓用戶選擇另存為的文件路徑和文件名
fileNameT = gui.SelectSaveFile("選擇文件……", "所有類型", "*")
if tk.IsErrorString(fileNameT) {
gui.SimpleError("錯誤提示", tk.GetErrorString(fileNameT))
return
}
textT = memo1.Text()
rs = tk.SaveStringToFile(textT, fileNameT)
if tk.IsErrorString(rs) {
gui.SimpleError("錯誤提示", tk.GetErrorString(rs))
return
}
// 提示已經(jīng)保存成功
gui.SimpleInfo("信息提示", "文件已保存至:" + fileNameT)
}
// 點按“關(guān)閉”的回到函數(shù)椿肩,結(jié)束圖形界面程序的運行
onButtonCloseClick = fn(sender) {
application.Terminate()
}
// 在主窗口增加“打開文件”的按鈕
buttonOpen = lcl.NewButton(mainForm)
buttonOpen.SetParent(mainForm)
buttonOpen.SetLeft(10)
buttonOpen.SetTop(435)
buttonOpen.SetCaption("打開文件")
buttonOpen.SetAnchors(lcl.NewSet(lcl.AkBottom, lcl.AkLeft))
buttonOpen.SetOnClick(lcl.NewTNotifyEvent(onButtonOpenClick))
// 在主窗口增加“另存”的按鈕
buttonSaveAs = lcl.NewButton(mainForm)
buttonSaveAs.SetParent(mainForm)
buttonSaveAs.SetLeft(110)
buttonSaveAs.SetTop(435)
buttonSaveAs.SetCaption("另存")
buttonSaveAs.SetAnchors(lcl.NewSet(lcl.AkBottom, lcl.AkLeft))
buttonSaveAs.SetOnClick(lcl.NewTNotifyEvent(onButtonSaveAsClick))
// 在主窗口增加“關(guān)閉”的按鈕
buttonClose = lcl.NewButton(mainForm)
buttonClose.SetParent(mainForm)
buttonClose.SetLeft(210)
buttonClose.SetTop(435)
buttonClose.SetCaption("關(guān)閉")
buttonClose.SetAnchors(lcl.NewSet(lcl.AkBottom, lcl.AkLeft))
buttonClose.SetOnClick(lcl.NewTNotifyEvent(onButtonCloseClick))
// 啟動圖形界面運行
application.Run()