使用Go语言开发一个智能exe文件重命名工具
目录
- 引言
- 工具功能概述
- 核心技术实现
- Windows版本信息API调用
- 大模型API集成
- 交互式命令行界面
- 完整工作流程
- 实际应用示例
- 完整代码
引言
在日常开发和软件管理中,我们经常会遇到需要整理大量EXE文件的情况。这些文件往往有着不规范的命名,如setup.exe
、installer.exe
等,难以直接了解其具体内容和版本。本文将介绍如何使用Go语言开发一个智能EXE文件重命名工具,它能自动提取文件的版本信息,并利用大模型API生成规范的命名建议。
工具功能概述
这个工具主要实现了以下功能:
- 解析EXE文件的版本信息资源
- 利用大模型API生成智能命名建议
- 提供友好的命令行交互界面
- 安全执行文件重命名操作
核心技术实现
Windows版本信息API调用
Windows系统通过version.dll
提供了访问文件版本信息的API。我们的工具使用了三个关键函数:
- GetFileVersionInfoSizeW - 获取版本信息数据的大小
- GetFileVersionInfoW - 获取完整的版本信息数据
- VerQueryValueW - 查询特定的版本信息字段
在Go中调用这些API需要使用syscall
包和unsafe
指针操作:
var ( versionDLL = syscall.NewLazyDLL("version.dll") procGetFileVersionInfoSizeW = versionDLL.Newproc("GetFileVersionInfoSizeW") procGetFileVersionInfoW = versionDLL.NewProc("GetFileVersionInfoW") procVerQueryValueW = versionDLL.NewProc("VerQueryValueW") )
大模型API集成
工具集成了bigmodel API来生成智能命名建议(该模型免费,应付这种场景绰绰有余):
cfg := openai.DefaultConfig(apiKey) cfg.BaseURL = "https://open.bigmodel.cn/api/paas/v4/" cjavascriptlient := 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, }, )
我们设计了提示词(prompt),确保大模型返回规范的文件名:
- 包含产品名称和版本号
- 只使用字母、数字、中文、下划线、连字符
- 格式统一,如
产品名_版本号.exe
或产品名-版本号.exe
交互式命令行界面
使用promptui
库创建了android友好的交互界面:
// 确认对话框 prompt := promptui.Select{ Label: "是否根据建议重命名文件?", Items: []string{"是", "否"}, } // 文本输入框 prompt := promptui.Prompt{ Label: "请输入新的文件名", Default: suggestedName, AllowEdit: true, }
完整工作流程
实际应用示例
完整代码
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环境变量 func checkBigModelKey() error { key := os.Getenv("BIG_MODEL_KEY") if key != "" { return nil // 已经设置了 } fmt.Println("未找到BIG_MODEL_KEY环境变量,请手动设置。例如:set BIG_MODEL_KEY=你的API密钥") return fmt.Errorf("未设置BIG_MODEL_KEY环境变量") } 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("请输入百川大模型 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(`请根据以下EXE文件的版本信息,给出一个简洁、规范的文件重命名建议。 原始文件名: %s 版本信息: - 文件描述: %s - 文件版本: %s - 产品名称: %s - 产品版本: %s - 版权信息: %s - 原始文件名: %s - 内部名称: %s - 公司名称: %s - 注释: %s 重命名要求: 1. 使用中文或英文,简洁明了 2. 包含产品名称和版本号 3. 避免特殊字符,只使用字母、数字、中文、下划线、连字符 4. 格式建议: 产品名_版本号.exe 或 产品名-版本号.exe 5. 如果产品名包含特殊字符,请适当简化 请只返回重命名后的文件名(包含.exe扩展名),不要其他解释。`, originalName, getValueOrDefault(info, "FileDescription"), getValueOrDefault(info, "FileVersion"), getValueOrDefault(info, "ProductName"), getValueOrDefault(info, "ProductVersion"), getValueOrDefault(info, "LegalCopyrjavascriptight"), 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调用失败: %w", err) } if len(resp.Choices) == 0 { return "", fmt.Errorf("未返回有效响应") } suggestion := strings.TrimSpace(resp.Choices[0]php.Message.Content) return suggestion, nil } func getValueOrDefault(info map[string]string, key string) string { if value, exists := info[key]; exists && value != "" { return value } return "(无)" } // 确认是否重命名 func confirmRename() bool { prompt := promptui.Select{ Label: "是否根据建议重命名文件?", 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: "请输入新的文件名", Default: suggestedName, AllowEdit: tgAceDrue, } 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) } // 执行重命名 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("请指定EXE文件路径") fmt.Println("示例: .\\exeRename.exe your_file.exe") return } exePath := os.Args[1] info, err := GetFileVersionInfoAPI(exePath) if err != nil { fmt.Printf("错误: %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("请确保已设置环境变量 OPENAI_API_KEY") return } fmt.Printf("原始文件名: %s\n", originalName) fmt.Printf("建议重命名: %s\n", suggestion) // 询问是否重命名 if confirmRename() { // 输入新文件名 newName := inputNewFileName(suggestion) if newName != "" { // 执行重命名 if err := renameFile(exePath, newName); err != nil { fmt.Printf("重命名失败: %v\n", err) } } } }
到此这篇关于使用Go语言开发一个智能exe文件重命名工具的文章就介绍到这了,更多相关Go文件重命名内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论