使用ReadFile读取文件后打印出现异常字符的问题排查
问题描述
我有一个内容为"SakyLabs. Hello, MDE"的纯文本文件,尝试使用ReadFile函数读取后打印,却得到如下输出:
?SakyLabs. Hello, MDE??²
请问我哪里操作出错了?
以下是我的代码:
// 用于CreateFileW HANDLE createFile; DWORD fileDesiredAccess = GENERIC_READ | GENERIC_WRITE; DWORD fileShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; DWORD creationDisposition = CREATE_ALWAYS; DWORD fileFlags = FILE_ATTRIBUTE_NORMAL; // ReadFile函数相关变量 BOOL readFile; DWORD lastError; DWORD fileSize; LPWSTR fileBuffer; DWORD bytesRead; if ((wcscmp(argv[1], L"-ef") == 0)) { createFile = CreateFileW(argv[2], fileDesiredAccess, fileShareMode, NULL, OPEN_EXISTING, fileFlags, NULL); // 获取扩展错误信息 lastError = GetLastError(); if (createFile == INVALID_HANDLE_VALUE) { wprintf(L"打开文件出错,错误码:%d ", lastError); ShowErrorMessage(lastError); return; } // 文件已打开!读取内容 fileSize = GetFileSize(createFile, NULL); fileBuffer = (LPWSTR)malloc((fileSize) + 1); if (fileBuffer == NULL) { wprintf(L"内存分配失败。\n"); CloseHandle(createFile); return; } readFile = ReadFile(createFile, fileBuffer, fileSize, &bytesRead, NULL); lastError = GetLastError(); if (!readFile) { wprintf(L"读取文件出错,错误码:%d ", lastError); ShowErrorMessage(lastError); CloseHandle(createFile); return; } fileBuffer[fileSize] = '\0'; // 显示内容 wprintf(L"%s \n", fileBuffer); free(fileBuffer); CloseHandle(createFile); } return 0; }
问题原因及解决方法
核心问题是文本编码不匹配,同时存在内存分配和字符串终止的错误:
具体错误点
- 编码不匹配:纯文本文件默认是ANSI或UTF-8(单字节/可变字节编码),但你用
LPWSTR(对应UTF-16双字节宽字符)存储读取的原始字节,并用wprintf(宽字符输出函数)打印,导致单字节数据被强行解析为双字节UTF-16,出现乱码。 - 内存分配错误:
LPWSTR每个字符占2字节,你按文件字节数fileSize分配内存,再加1个单字节空间,既不符合宽字符内存需求,也会导致内存不足。 - 字符串终止错误:
fileBuffer[fileSize] = '\0';中fileSize是字节数,但LPWSTR的索引是按字符数(每个2字节),导致终止符位置错误,引发越界或无效宽字符。 - 错误码调用时机错误:
CreateFileW成功后仍调用GetLastError,会覆盖原本的0值或残留之前的错误码,应该只在createFile == INVALID_HANDLE_VALUE时调用。
解决方法(二选一)
方法1:使用ANSI字符集处理(适合普通纯文本)
将宽字符相关类型和函数替换为ANSI版本,匹配普通纯文本的编码:
// 修改变量类型 LPSTR fileBuffer; // ... // 内存分配按字节数+1 fileBuffer = (LPSTR)malloc(fileSize + 1); // ... // 用实际读取的字节数设置终止符 fileBuffer[bytesRead] = '\0'; // 用ANSI输出函数 printf("%s \n", fileBuffer);
方法2:UTF-8转UTF-16处理(推荐,支持多语言)
如果文件是UTF-8编码,先读取原始字节流,再转换为UTF-16宽字符后输出:
// 读取UTF-8原始字节 LPSTR utf8Buffer = (LPSTR)malloc(fileSize + 1); ReadFile(createFile, utf8Buffer, fileSize, &bytesRead, NULL); utf8Buffer[bytesRead] = '\0'; // 计算需要的宽字符缓冲区大小 int wcharSize = MultiByteToWideChar(CP_UTF8, 0, utf8Buffer, -1, NULL, 0); LPWSTR wcharBuffer = (LPWSTR)malloc(wcharSize * sizeof(WCHAR)); // 转换为UTF-16 MultiByteToWideChar(CP_UTF8, 0, utf8Buffer, -1, wcharBuffer, wcharSize); // 输出宽字符内容 wprintf(L"%s \n", wcharBuffer); // 释放内存 free(utf8Buffer); free(wcharBuffer);
额外注意事项
- 读取文件时优先使用
bytesRead(实际读取字节数)而非fileSize,避免因文件系统或权限问题导致读取不完整。 - 调用
GetLastError必须紧跟在失败的API调用之后,否则错误码会被后续操作覆盖。
内容的提问来源于stack exchange,提问作者Sergio Calderon
相关产品推荐
相关产品推荐

