使用Go語言開發(fā)一個(gè)智能exe文件重命名工具
引言
在日常開發(fā)和軟件管理中,我們經(jīng)常會(huì)遇到需要整理大量EXE文件的情況。這些文件往往有著不規(guī)范的命名,如setup.exe、installer.exe等,難以直接了解其具體內(nèi)容和版本。本文將介紹如何使用Go語言開發(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)通過version.dll提供了訪問文件版本信息的API。我們的工具使用了三個(gè)關(guān)鍵函數(shù):
- GetFileVersionInfoSizeW - 獲取版本信息數(shù)據(jù)的大小
- GetFileVersionInfoW - 獲取完整的版本信息數(shù)據(jù)
- VerQueryValueW - 查詢特定的版本信息字段
在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來生成智能命名建議(該模型免費(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)品名稱和版本號(hào)
- 只使用字母、數(shù)字、中文、下劃線、連字符
- 格式統(tǒng)一,如
產(chǎn)品名_版本號(hào).exe或產(chǎn)品名-版本號(hào).exe
交互式命令行界面
使用promptui庫創(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")
}
// 查詢語言和代碼頁
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è)簡潔、規(guī)范的文件重命名建議。
原始文件名: %s
版本信息:
- 文件描述: %s
- 文件版本: %s
- 產(chǎn)品名稱: %s
- 產(chǎn)品版本: %s
- 版權(quán)信息: %s
- 原始文件名: %s
- 內(nèi)部名稱: %s
- 公司名稱: %s
- 注釋: %s
重命名要求:
1. 使用中文或英文,簡潔明了
2. 包含產(chǎn)品名稱和版本號(hào)
3. 避免特殊字符,只使用字母、數(shù)字、中文、下劃線、連字符
4. 格式建議: 產(chǎn)品名_版本號(hào).exe 或 產(chǎn)品名-版本號(hào).exe
5. 如果產(chǎn)品名包含特殊字符,請(qǐng)適當(dāng)簡化
請(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 "(無)"
}
// 確認(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 = "(無)"
}
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)
// 詢問是否重命名
if confirmRename() {
// 輸入新文件名
newName := inputNewFileName(suggestion)
if newName != "" {
// 執(zhí)行重命名
if err := renameFile(exePath, newName); err != nil {
fmt.Printf("重命名失敗: %v\n", err)
}
}
}
}
到此這篇關(guān)于使用Go語言開發(fā)一個(gè)智能exe文件重命名工具的文章就介紹到這了,更多相關(guān)Go文件重命名內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GoLand一鍵上傳項(xiàng)目到遠(yuǎn)程服務(wù)器的方法步驟
我們開發(fā)項(xiàng)目常常將項(xiàng)目上傳到linux遠(yuǎn)程服務(wù)器上來運(yùn)行,本文主要介紹了GoLand一鍵上傳項(xiàng)目到遠(yuǎn)程服務(wù)器的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
Go語言HTTPServer開發(fā)的六種方式小結(jié)
Golang的Server開發(fā)顯得非常簡單,有很多種方式,本文就介紹了Go語言HTTPServer開發(fā)的六種方式,具有一定的參考價(jià)值,感興趣的可以了解一下2021-11-11
golang實(shí)現(xiàn)aes-cbc-256加密解密功能
這篇文章主要介紹了golang實(shí)現(xiàn)aes-cbc-256加密解密功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
Golang中自定義json序列化時(shí)間格式的示例代碼
Go語言作為一個(gè)由Google開發(fā),號(hào)稱互聯(lián)網(wǎng)的C語言的語言,自然也對(duì)JSON格式支持很好,下面這篇文章主要介紹了關(guān)于Golang中自定義json序列化時(shí)間格式的相關(guān)內(nèi)容,下面話不多說了,來一起看看詳細(xì)的介紹吧2024-08-08
go zero微服務(wù)高在請(qǐng)求量下如何優(yōu)化
這篇文章主要為大家介紹了go zero微服務(wù)高在請(qǐng)求量下的優(yōu)化處理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
golang實(shí)現(xiàn)LRU緩存淘汰算法的示例代碼
這篇文章主要介紹了golang實(shí)現(xiàn)LRU緩存淘汰算法的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12
關(guān)于golang監(jiān)聽rabbitmq消息隊(duì)列任務(wù)斷線自動(dòng)重連接的問題
這篇文章主要介紹了golang監(jiān)聽rabbitmq消息隊(duì)列任務(wù)斷線自動(dòng)重連接,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03

