Windows 中的 ReadFile()

Posted

技术标签:

【中文标题】Windows 中的 ReadFile()【英文标题】:ReadFile() in Windows 【发布时间】:2017-09-19 09:52:17 【问题描述】:

我正在尝试使用 Windows 函数 ReadFile() 从文件中读取,但是当我打印消息时,它会打印太多字符。

无论我从 ANSII 文件还是 UNICODE 文件中读取,我都没有得到正确的字符。

文件中的文本是:“这是一个文本文件”。

ANSII 文件的屏幕截图:

UNICODE 文件的屏幕截图:

我做错了什么?

#define BUFSIZE 4000


int _tmain(int argc, TCHAR *argv[])

    HANDLE  hIn;
    TCHAR buffer[BUFSIZE];
    DWORD nIn = 0;

    //create file
    hIn = CreateFile(argv[1],
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    //check the handle
    if (hIn == INVALID_HANDLE_VALUE)
    
        printf("\nOpen file error\n");
    
    //read from file
    if (FALSE == ReadFile(hIn, buffer, BUFSIZE - 1, &nIn, NULL))
    
        printf("Terminal failure: Unable to read from file.\n GetLastError=%08x\n", GetLastError());
        CloseHandle(hIn);
        return 0;
    

    if (nIn > 0 && nIn <= BUFSIZE - 1)
    
        buffer[nIn] = TEXT('\0'); // NULL character
        _tprintf(TEXT("Data read from %s (%d bytes): \n"), argv[1], nIn);
    
    else if (nIn == 0)
    
        _tprintf(TEXT("No data read from file %s\n"), argv[1]);
    
    else
    
        printf("\n ** Unexpected value for nIn ** \n");
    
    printf("1:%s\n", buffer);
    _tprintf(TEXT("\n2:%s"), buffer);

    return 0;

【问题讨论】:

你为什么使用 TCHAR?你在Win98上运行吗? 不,但要制作一个通用程序,用于简单的字符或宽字符。 使用TCHAR是错误的。明确。 TCHAR 只会让您感到困惑,尤其是因为您还没有真正清楚地理解文本编码。额外的间接性只会让您更加困惑。 【参考方案1】:

Windows API 函数 ReadFile() 读取字节,unsigned char,而不是 Windows UNICODE 大小的 TCHAR,在现代 Windows 中是两个字节,而不是在 Windows 95 中的一个字节等。所以你需要进行以下修改。

另请参阅What is the difference between _tmain() and main() in C++?,其中包含有关 Windows 的不同编译目标和使用的字符编码的一些附加信息。

首先,您的缓冲区应该是 BYTE 类型,而不是 TCHAR

其次,您需要确保它是零字段,因此像 BYTE buffer[BUFSIZE] = 0; 一样初始化缓冲区。

由于 Windows UNICODE 是 UTF-16 或每个字符两个字节,您需要确保 UNICODE 文本字符串的字符串结尾字符是二进制零的两个字节,并且您需要考虑到缓冲区长度。放置字符串结尾时,您需要确保它是两个零字节,而不仅仅是一个。

您应该阅读BUFSIZE - 2 字节,以确保您读取的字节数是偶数,以防您正在阅读的是UNICODE 字符串。你的缓冲区大小也应该是 2 的倍数。

如果字符串是您读入的 ANSI 字符串,那么当显示为 UNICODE 时,它可能看起来像垃圾,因为每个 UNICODE 字符将由两个 ANSI 字符组成。

因此,要使字符串相同,您需要在两种字符编码之间进行转换。请参阅这篇关于在文本文件中使用Byte Order Marks 来指示文件中使用的字符编码类型的文章。

【讨论】:

哎呀。它实际上不必是零填充,它只需要零终止。你的方法行得通,但我过早的优化强迫是刺痛的。在ReadFile 返回时获得实际缓冲区长度后,将 NUL 字符附加到缓冲区的末尾就足够了。另外,我知道你在这里试图让事情变得简单,而事实是极其复杂和令人沮丧的,但基本上不可能确定文件使用什么字符编码。有一些启发式方法,但它们不可靠。您需要被元数据或用户告知。 @CodyGray 所以我知道它不需要填充零,但另一方面,从已知状态开始有助于在调试器中调试和查看数据结构。并且在第一次读取之后,无论如何它不再是零填充。我知道试图通过检查字节流来确定字符编码基本上是不可能的。这就是http协议中存在Content-Encoding:Content-Type:的原因。但是,由于这是他的文件,因此他可以使用字节顺序标记或文件扩展名或他想要使用的任何其他方法。 “我知道试图通过检查字节流来确定字符编码基本上是不可能的。这就是为什么Content-Encoding:Content-Type:存在于http协议中的原因。” -嗯,没有。这就是 BOM 存在的原因。缺少这一点,您可以对缓冲区运行一些启发式方法,例如打电话给IsTextUnicode。此外,没有称为 "ANSII" 的编码,TCHAR 不是 Unicode 代码单元(wchar_t 是)。 @IInspectable BOM 仅适用于 UTF 编码,不适用于 ANSI 编码。 ANSI 编码比 UTF 多得多。被明确告知文件编码比猜测要好。而IsTextUnicode() 正在猜测(并且众所周知有时会猜测错误)。在 UNICODE 编译中,TCHARwchar_t @RemyLebeau:“每条鱼都是金鱼(只要你只考虑金鱼)。” - 如果你遵循这种思路,那么是的,每个 @987654341 @ 是 wchar_t

以上是关于Windows 中的 ReadFile()的主要内容,如果未能解决你的问题,请参考以下文章

怎样使用ReadFile读取文本文件?

非阻塞IO可以等同异步IO嘛?

当我在 Windows 上的 HID 设备上执行 ReadFile() 时会发生啥?

打破 ReadFile() 阻塞 - 命名管道 (Windows API)

如何更改通过 ReadFile 函数读取的文本

在不使用 readfile() 的情况下检测 Windows 句柄上的空缓冲区