使用Go語(yǔ)言開(kāi)發(fā)一個(gè)智能exe文件重命名工具
引言
在日常開(kāi)發(fā)和軟件管理中,我們經(jīng)常會(huì)遇到需要整理大量EXE文件的情況。這些文件往往有著不規(guī)范的命名,如setup.exe
、installer.exe
等,難以直接了解其具體內(nèi)容和版本。本文將介紹如何使用Go語(yǔ)言開(kāi)發(fā)一個(gè)智能EXE文件重命名工具,它能自動(dòng)提取文件的版本信息,并利用大模型API生成規(guī)范的命名建議。
工具功能概述
這個(gè)工具主要實(shí)現(xiàn)了以下功能:
- 解析EXE文件的版本信息資源
- 利用大模型API生成智能命名建議
- 提供友好的命令行交互界面
- 安全執(zhí)行文件重命名操作
核心技術(shù)實(shí)現(xiàn)
Windows版本信息API調(diào)用
Windows系統(tǒng)通過(guò)version.dll
提供了訪問(wèn)文件版本信息的API。我們的工具使用了三個(gè)關(guān)鍵函數(shù):
- GetFileVersionInfoSizeW - 獲取版本信息數(shù)據(jù)的大小
- GetFileVersionInfoW - 獲取完整的版本信息數(shù)據(jù)
- VerQueryValueW - 查詢(xún)特定的版本信息字段
在Go中調(diào)用這些API需要使用syscall
包和unsafe
指針操作:
var ( versionDLL = syscall.NewLazyDLL("version.dll") procGetFileVersionInfoSizeW = versionDLL.NewProc("GetFileVersionInfoSizeW") procGetFileVersionInfoW = versionDLL.NewProc("GetFileVersionInfoW") procVerQueryValueW = versionDLL.NewProc("VerQueryValueW") )
大模型API集成
工具集成了bigmodel API來(lái)生成智能命名建議(該模型免費(fèi),應(yīng)付這種場(chǎng)景綽綽有余):
cfg := openai.DefaultConfig(apiKey) cfg.BaseURL = "https://open.bigmodel.cn/api/paas/v4/" client := openai.NewClientWithConfig(cfg) resp, err := client.CreateChatCompletion( context.Background(), openai.ChatCompletionRequest{ Model: "GLM-4-Flash-250414", Messages: []openai.ChatCompletionMessage{ { Role: openai.ChatMessageRoleUser, Content: prompt, }, }, MaxTokens: 100, Temperature: 0.3, }, )
我們?cè)O(shè)計(jì)了提示詞(prompt),確保大模型返回規(guī)范的文件名:
- 包含產(chǎn)品名稱(chēng)和版本號(hào)
- 只使用字母、數(shù)字、中文、下劃線、連字符
- 格式統(tǒng)一,如
產(chǎn)品名_版本號(hào).exe
或產(chǎn)品名-版本號(hào).exe
交互式命令行界面
使用promptui
庫(kù)創(chuàng)建了友好的交互界面:
// 確認(rèn)對(duì)話框 prompt := promptui.Select{ Label: "是否根據(jù)建議重命名文件?", Items: []string{"是", "否"}, } // 文本輸入框 prompt := promptui.Prompt{ Label: "請(qǐng)輸入新的文件名", Default: suggestedName, AllowEdit: true, }
完整工作流程
實(shí)際應(yīng)用示例
完整代碼
package main import ( "context" "fmt" "os" "path/filepath" "strings" "syscall" "unicode/utf16" "unsafe" "github.com/manifoldco/promptui" openai "github.com/sashabaranov/go-openai" ) var ( versionDLL = syscall.NewLazyDLL("version.dll") procGetFileVersionInfoSizeW = versionDLL.NewProc("GetFileVersionInfoSizeW") procGetFileVersionInfoW = versionDLL.NewProc("GetFileVersionInfoW") procVerQueryValueW = versionDLL.NewProc("VerQueryValueW") ) // 檢查BIG_MODEL_KEY環(huán)境變量 func checkBigModelKey() error { key := os.Getenv("BIG_MODEL_KEY") if key != "" { return nil // 已經(jīng)設(shè)置了 } fmt.Println("未找到BIG_MODEL_KEY環(huán)境變量,請(qǐng)手動(dòng)設(shè)置。例如:set BIG_MODEL_KEY=你的API密鑰") return fmt.Errorf("未設(shè)置BIG_MODEL_KEY環(huán)境變量") } func utf16PtrFromString(s string) *uint16 { u := utf16.Encode([]rune(s + "\x00")) return &u[0] } // Windows API方式獲取版本信息 func GetFileVersionInfoAPI(path string) (map[string]string, error) { info := make(map[string]string) pPath := utf16PtrFromString(path) var handle uint32 size, _, _ := procGetFileVersionInfoSizeW.Call( uintptr(unsafe.Pointer(pPath)), uintptr(unsafe.Pointer(&handle)), ) if size == 0 { return nil, fmt.Errorf("GetFileVersionInfoSizeW failed") } buf := make([]byte, size) ret, _, _ := procGetFileVersionInfoW.Call( uintptr(unsafe.Pointer(pPath)), 0, uintptr(size), uintptr(unsafe.Pointer(&buf[0])), ) if ret == 0 { return nil, fmt.Errorf("GetFileVersionInfoW failed") } // 查詢(xún)語(yǔ)言和代碼頁(yè) var transPtr uintptr var transLen uint32 subBlock := utf16PtrFromString(`\VarFileInfo\Translation`) ret, _, _ = procVerQueryValueW.Call( uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(subBlock)), uintptr(unsafe.Pointer(&transPtr)), uintptr(unsafe.Pointer(&transLen)), ) if ret == 0 || transLen < 4 { return nil, fmt.Errorf("VerQueryValueW Translation failed") } lang := *(*uint16)(unsafe.Pointer(transPtr)) codepage := *(*uint16)(unsafe.Pointer(transPtr + 2)) langCode := fmt.Sprintf("%04x%04x", lang, codepage) fields := []string{ "FileDescription", "FileVersion", "ProductName", "ProductVersion", "LegalCopyright", "OriginalFilename", "InternalName", "CompanyName", "Comments", } for _, field := range fields { block := fmt.Sprintf(`\StringFileInfo\%s\%s`, langCode, field) blockPtr := utf16PtrFromString(block) var valuePtr uintptr var valueLen uint32 ret, _, _ := procVerQueryValueW.Call( uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(blockPtr)), uintptr(unsafe.Pointer(&valuePtr)), uintptr(unsafe.Pointer(&valueLen)), ) if ret != 0 && valueLen > 0 { val := syscall.UTF16ToString((*[1 << 16]uint16)(unsafe.Pointer(valuePtr))[:valueLen]) info[field] = val } } return info, nil } func getAPIKey() string { key := os.Getenv("BIG_MODEL_KEY") if key != "" { return key } fmt.Print("請(qǐng)輸入百川大模型 API KEY(BIG_MODEL_KEY):") var input string fmt.Scanln(&input) return strings.TrimSpace(input) } func GetRenameSuggestion(info map[string]string, originalName string) (string, error) { apiKey := getAPIKey() if apiKey == "" { return "", fmt.Errorf("未提供 BIG_MODEL_KEY") } cfg := openai.DefaultConfig(apiKey) cfg.BaseURL = "https://open.bigmodel.cn/api/paas/v4/" client := openai.NewClientWithConfig(cfg) prompt := fmt.Sprintf(`請(qǐng)根據(jù)以下EXE文件的版本信息,給出一個(gè)簡(jiǎn)潔、規(guī)范的文件重命名建議。 原始文件名: %s 版本信息: - 文件描述: %s - 文件版本: %s - 產(chǎn)品名稱(chēng): %s - 產(chǎn)品版本: %s - 版權(quán)信息: %s - 原始文件名: %s - 內(nèi)部名稱(chēng): %s - 公司名稱(chēng): %s - 注釋: %s 重命名要求: 1. 使用中文或英文,簡(jiǎn)潔明了 2. 包含產(chǎn)品名稱(chēng)和版本號(hào) 3. 避免特殊字符,只使用字母、數(shù)字、中文、下劃線、連字符 4. 格式建議: 產(chǎn)品名_版本號(hào).exe 或 產(chǎn)品名-版本號(hào).exe 5. 如果產(chǎn)品名包含特殊字符,請(qǐng)適當(dāng)簡(jiǎn)化 請(qǐng)只返回重命名后的文件名(包含.exe擴(kuò)展名),不要其他解釋。`, originalName, getValueOrDefault(info, "FileDescription"), getValueOrDefault(info, "FileVersion"), getValueOrDefault(info, "ProductName"), getValueOrDefault(info, "ProductVersion"), getValueOrDefault(info, "LegalCopyright"), getValueOrDefault(info, "OriginalFilename"), getValueOrDefault(info, "InternalName"), getValueOrDefault(info, "CompanyName"), getValueOrDefault(info, "Comments")) resp, err := client.CreateChatCompletion( context.Background(), openai.ChatCompletionRequest{ Model: "GLM-4-Flash-250414", Messages: []openai.ChatCompletionMessage{ { Role: openai.ChatMessageRoleUser, Content: prompt, }, }, MaxTokens: 100, Temperature: 0.3, }, ) if err != nil { return "", fmt.Errorf("API調(diào)用失敗: %w", err) } if len(resp.Choices) == 0 { return "", fmt.Errorf("未返回有效響應(yīng)") } suggestion := strings.TrimSpace(resp.Choices[0].Message.Content) return suggestion, nil } func getValueOrDefault(info map[string]string, key string) string { if value, exists := info[key]; exists && value != "" { return value } return "(無(wú))" } // 確認(rèn)是否重命名 func confirmRename() bool { prompt := promptui.Select{ Label: "是否根據(jù)建議重命名文件?", Items: []string{"是", "否"}, } _, result, err := prompt.Run() if err != nil { fmt.Printf("選擇失敗: %v\n", err) return false } return result == "是" } // 輸入新文件名 func inputNewFileName(suggestedName string) string { prompt := promptui.Prompt{ Label: "請(qǐng)輸入新的文件名", Default: suggestedName, AllowEdit: true, } result, err := prompt.Run() if err != nil { fmt.Printf("輸入失敗: %v\n", err) return "" } return result } // 重命名文件 func renameFile(oldPath, newName string) error { dir := filepath.Dir(oldPath) newPath := filepath.Join(dir, newName) // 檢查新文件名是否已存在 if _, err := os.Stat(newPath); err == nil { return fmt.Errorf("文件 %s 已存在", newName) } // 執(zhí)行重命名 err := os.Rename(oldPath, newPath) if err != nil { return fmt.Errorf("重命名失敗: %w", err) } fmt.Printf("文件已重命名為: %s\n", newName) return nil } func main() { // 檢查BIG_MODEL_KEY if err := checkBigModelKey(); err != nil { return } if len(os.Args) < 2 { fmt.Println("請(qǐng)指定EXE文件路徑") fmt.Println("示例: .\\exeRename.exe your_file.exe") return } exePath := os.Args[1] info, err := GetFileVersionInfoAPI(exePath) if err != nil { fmt.Printf("錯(cuò)誤: %v\n", err) return } // 顯示版本信息 fmt.Println("=== 版本信息 ===") fields := []string{"FileDescription", "FileVersion", "ProductName", "ProductVersion", "LegalCopyright", "OriginalFilename", "InternalName", "CompanyName", "Comments"} for _, field := range fields { value := info[field] if value == "" { value = "(無(wú))" } fmt.Printf("%-20s: %s\n", field, value) } // 獲取原始文件名(不含路徑) originalName := exePath if lastSlash := strings.LastIndex(exePath, "\\"); lastSlash != -1 { originalName = exePath[lastSlash+1:] } if lastSlash := strings.LastIndex(originalName, "/"); lastSlash != -1 { originalName = originalName[lastSlash+1:] } // 獲取重命名建議 fmt.Println("\n=== 智能重命名建議 ===") suggestion, err := GetRenameSuggestion(info, originalName) if err != nil { fmt.Printf("獲取重命名建議失敗: %v\n", err) fmt.Println("請(qǐng)確保已設(shè)置環(huán)境變量 OPENAI_API_KEY") return } fmt.Printf("原始文件名: %s\n", originalName) fmt.Printf("建議重命名: %s\n", suggestion) // 詢(xún)問(wèn)是否重命名 if confirmRename() { // 輸入新文件名 newName := inputNewFileName(suggestion) if newName != "" { // 執(zhí)行重命名 if err := renameFile(exePath, newName); err != nil { fmt.Printf("重命名失敗: %v\n", err) } } } }
到此這篇關(guān)于使用Go語(yǔ)言開(kāi)發(fā)一個(gè)智能exe文件重命名工具的文章就介紹到這了,更多相關(guān)Go文件重命名內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GoLand一鍵上傳項(xiàng)目到遠(yuǎn)程服務(wù)器的方法步驟
我們開(kāi)發(fā)項(xiàng)目常常將項(xiàng)目上傳到linux遠(yuǎn)程服務(wù)器上來(lái)運(yùn)行,本文主要介紹了GoLand一鍵上傳項(xiàng)目到遠(yuǎn)程服務(wù)器的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06Go語(yǔ)言HTTPServer開(kāi)發(fā)的六種方式小結(jié)
Golang的Server開(kāi)發(fā)顯得非常簡(jiǎn)單,有很多種方式,本文就介紹了Go語(yǔ)言HTTPServer開(kāi)發(fā)的六種方式,具有一定的參考價(jià)值,感興趣的可以了解一下2021-11-11golang實(shí)現(xiàn)aes-cbc-256加密解密功能
這篇文章主要介紹了golang實(shí)現(xiàn)aes-cbc-256加密解密功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04Golang中自定義json序列化時(shí)間格式的示例代碼
Go語(yǔ)言作為一個(gè)由Google開(kāi)發(fā),號(hào)稱(chēng)互聯(lián)網(wǎng)的C語(yǔ)言的語(yǔ)言,自然也對(duì)JSON格式支持很好,下面這篇文章主要介紹了關(guān)于Golang中自定義json序列化時(shí)間格式的相關(guān)內(nèi)容,下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧2024-08-08go zero微服務(wù)高在請(qǐng)求量下如何優(yōu)化
這篇文章主要為大家介紹了go zero微服務(wù)高在請(qǐng)求量下的優(yōu)化處理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07golang實(shí)現(xiàn)LRU緩存淘汰算法的示例代碼
這篇文章主要介紹了golang實(shí)現(xiàn)LRU緩存淘汰算法的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12關(guān)于golang監(jiān)聽(tīng)rabbitmq消息隊(duì)列任務(wù)斷線自動(dòng)重連接的問(wèn)題
這篇文章主要介紹了golang監(jiān)聽(tīng)rabbitmq消息隊(duì)列任務(wù)斷線自動(dòng)重連接,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03