Windows下Go读取记事本Bin文件编码异常求助
问题:Windows记事本Bin文件解码后字符间出现多余空格
读取%LOCALAPPDATA%\Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState下的.bin文件时,解码内容显示为r e g e r g,而非预期的regerg,尝试多种编码方式未解决,相关代码如下:
package main import ( "bytes" "encoding/binary" "io/ioutil" "os" "path/filepath" "strings" ) func utf16Decode(b []byte) string { var str strings.Builder r := bytes.NewReader(b) var utf16Bytes [2]byte for { if _, err := r.Read(utf16Bytes[:1]); err != nil { break } unicodeChar := binary.LittleEndian.Uint16(utf16Bytes[:]) str.WriteRune(rune(unicodeChar)) } return str.String() } func write(contentToAppend string) { tempDir := os.Getenv("TEMP") + "\\RnsLMp_LOG" // Adjust for your OS if needed err := os.MkdirAll(tempDir, 0755) if err != nil { return } err = os.WriteFile(tempDir+"\\test.txt", []byte(contentToAppend), 0644) if err != nil { return } } func clear(filename string) { tempDir := os.Getenv("TEMP") + "\\RnsLMp_LOG" // Adjust for your OS if needed err := os.MkdirAll(tempDir, 0755) if err != nil { return } err = os.WriteFile(tempDir+"\\test.txt", []byte("e"), 0644) if err != nil { return } } func main() { defer func() { if r := recover(); r != nil { return } }() appDataDir := os.Getenv("LOCALAPPDATA") directoryRelative := `Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState` fullPath := filepath.Join(appDataDir, directoryRelative) listOfBinFiles, _ := filepath.Glob(filepath.Join(fullPath, "*.bin")) for _, fullFilePath := range listOfBinFiles { if strings.HasSuffix(fullFilePath, ".0.bin") || strings.HasSuffix(fullFilePath, ".1.bin") { continue } contents, _ := ioutil.ReadFile(fullFilePath) is_saved_file := contents[3] if string(is_saved_file) != "" { lengthOfFilename := int(contents[4]) filenameEnding := 5 + lengthOfFilename*2 originalFilename := string(contents[5:filenameEnding]) delimeterStart := bytes.Index(contents[filenameEnding:], []byte{0x00, 0x01}) + filenameEnding delimeterEnd := bytes.Index(contents[filenameEnding:], []byte{0x01, 0x00, 0x00, 0x00}) + filenameEnding fileMarker := contents[delimeterStart+2 : delimeterEnd] fileMarker = fileMarker[:len(fileMarker)/2] originalFileContents := contents[delimeterEnd+4+len(fileMarker) : len(contents)-5] decodedContent := utf16Decode(originalFileContents) decodedContent = strings.ReplaceAll(decodedContent, "\r", "\n") data := "\n" + originalFilename + "\n" + decodedContent + "\n" + strings.Repeat("=", 25) write(data) } else { lengthOfFilename := int(contents[4]) filenameEnding := 5 + lengthOfFilename*2 originalFilename := string(contents[5:filenameEnding]) delimeterStart := bytes.Index(contents, []byte{0x00, 0x01}) delimeterEnd := bytes.Index(contents, []byte{0x01, 0x00, 0x00, 0x00}) fileMarker := contents[delimeterStart+2 : delimeterEnd] fileMarker = fileMarker[:len(fileMarker)/2] originalFileContents := contents[delimeterEnd+4+len(fileMarker) : len(contents)-5] decodedContent := utf16Decode(originalFileContents) decodedContent = strings.ReplaceAll(decodedContent, "\r", "\n") data := "\n" + originalFilename + "\n" + decodedContent + "\n" + strings.Repeat("=", 25) write(data) } } }
问题原因
自定义的utf16Decode函数存在逻辑错误:每次仅读取1个字节到2字节数组中,第二个字节始终保留初始值0。而记事本的.bin文件采用UTF-16LE编码(每个字符占2字节,英文字符高字节为0),这会导致函数把每个字符的高字节(0x00)当成独立字符解析,生成U+0000空字符,最终显示为字符间的空格。
修复方案
方案1:修复自定义解码函数
修改utf16Decode函数,每次读取完整的2字节来解析一个UTF-16LE字符:
func utf16Decode(b []byte) string { var str strings.Builder r := bytes.NewReader(b) var utf16Bytes [2]byte for { // 每次读取完整的2字节,而非1字节 n, err := r.Read(utf16Bytes[:]) if err != nil || n != 2 { break } unicodeChar := binary.LittleEndian.Uint16(utf16Bytes[:]) str.WriteRune(rune(unicodeChar)) } return str.String() }
方案2:使用标准库实现更可靠解码
直接利用Go标准库的unicode/utf16包处理UTF-16编码转换,避免手动处理的潜在错误:
import "unicode/utf16" func utf16Decode(b []byte) string { // 将UTF-16LE字节数组转换为uint16切片 var u16 []uint16 for i := 0; i < len(b); i += 2 { if i+1 >= len(b) { break } u16 = append(u16, binary.LittleEndian.Uint16(b[i:i+2])) } // 将UTF-16编码转换为Go字符串 return string(utf16.Decode(u16)) }
额外优化点
- 添加错误处理:代码中大量忽略
ioutil.ReadFile、filepath.Glob等操作的错误,建议补充错误处理逻辑,便于调试问题。 - 文件名解码修复:当前
originalFilename直接将UTF-16LE字节转字符串,同样会出现空格问题,需用修复后的utf16Decode函数处理:
// 替换原代码中的originalFilename赋值 originalFilenameBytes := contents[5:filenameEnding] originalFilename := utf16Decode(originalFilenameBytes)
内容的提问来源于stack exchange,提问作者Loona
相关产品推荐
相关产品推荐

