文件大小大于应有的大小,将添加额外的新行

File size is larger than it should, extra new lines are added

本文关键字:添加 新行 大于 文件大小      更新时间:2023-10-16

简介:

我正在用ReadFile读取文本文件。传递到ReadFile的缓冲区被发送到具有cout的标准输出。标准输出被重定向到文本文件。

问题:

虽然我的代码"有效",但没有数据丢失,生成的文件比原始文件大。

当在记事本中打开时,一切似乎都很好,但当在notepad++中打开时我可以清楚地看到添加了额外的行。这些线是新的线(n(。

下面提交了再现此行为的MVCE。

#include <iostream>
#include <Windows.h>
int main()
{
    HANDLE hFile = ::CreateFile("C:\123.txt", 
        GENERIC_READ,
        FILE_SHARE_READ | 
        FILE_SHARE_WRITE | 
        FILE_SHARE_DELETE,
        NULL, 
        OPEN_EXISTING, 
        FILE_ATTRIBUTE_NORMAL, 
        NULL);
    if (INVALID_HANDLE_VALUE == hFile) 
        return ::GetLastError();
    char buffer[256];
    DWORD bytesRead = 1,  // dummy value so while loop can work
        bytesWritten = 0; // needed for WriteFile, not for cout version
    //======== so WriteFile outputs to console, not needed for cout version
    HANDLE hStandardOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
    if (INVALID_HANDLE_VALUE == hStandardOutput)
    {
        std::cout << "GetStdHandle error code = " << ::GetLastError() << std::endl;
        ::CloseHandle(hFile);
        return ::GetLastError();
    }
    //============================
    while(bytesRead)
    {
        // '' terminate buffer, needed for cout only
        ::memset(buffer, '', sizeof(buffer)); 
        if (!::ReadFile(hFile, 
            buffer, 
            sizeof(buffer) - 1, // - 1 for '', not needed when using WriteFile
            &bytesRead, NULL))
        {
            std::cout << "ReadFile error code = " << ::GetLastError() << std::endl;
            break;
        }
        /*============= Works fine
        if(!::WriteFile(hStandardOutput, buffer, bytesRead, &bytesWritten, NULL))
        {
            std::cout << "WriteFile error code = " << ::GetLastError() << std::endl;
            break;
        }*/
        //------------- comment out when testing WriteFile 
        std::cout << buffer;  // extra lines...
        // std::cout.write(buffer, bytesRead); // extra lines as well...
        //----------------------------------------
    }
    ::CloseHandle(hFile);
    return 0;
}

问题:

是什么导致了上述行为?如何修复?

我为解决问题所做的努力:

当我输入这篇文章时,我在谷歌上漫无目的地搜索,希望能找到一些线索。

我怀疑问题出在输出n时,Windows似乎也插入了r,但我不确定。

n字符对STL字符流具有特殊意义。它表示一条换行符,在输出时会被转换为特定于平台的换行符。此处讨论:

二进制和文本模式

文本流是由行组成的有序字符序列(零个或多个字符加上终止的'n'(。最后一行是否需要终止'n'由实现定义。输入和输出时可能需要添加、更改或删除字符,以符合操作系统中表示文本的约定(特别是,Windows操作系统上的C流在输出时将n转换为rn,在输入时将rn转换为n(。

因此,很可能std::cout在被赋予n时输出rn,即使之前的r也被赋予,因此rn的输入在输出时可以变成rrn。在Windows上,单个应用程序如何处理裸露的CR字符并不是标准行为。它们可能被忽略,也可能被视为换行符。在你的情况下,听起来像是后者。

没有标准方式在二进制模式中使用std::cout,因此n被输出为n而不是rn。但是,请参阅如何使cout在二进制模式下运行?了解在Windows上以二进制模式输出std::cout的一些可能方法,具体取决于编译器和STL实现。或者,您可以尝试使用std::cout.rdbuf()来替换自己的std::basic_streambuf对象,该对象执行到控制台的二进制输出。

也就是说,你的代码处理数据缓冲区的方式有点偏离,它应该看起来更像这样(不考虑以上信息(:

#include <iostream>
#include <Windows.h>
int main()
{
    HANDLE hFile = ::CreateFile("C:\123.txt", 
        GENERIC_READ,
        FILE_SHARE_READ | 
        FILE_SHARE_WRITE |
        FILE_SHARE_DELETE,  // why??
        NULL, 
        OPEN_EXISTING, 
        FILE_ATTRIBUTE_NORMAL, 
        NULL);
    if (INVALID_HANDLE_VALUE == hFile) 
        return ::GetLastError();
    char buffer[256];
    DWORD bytesRead, bytesWritten, err;
    //======== so WriteFile outputs to console, not needed for cout version
    HANDLE hStandardOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
    if (INVALID_HANDLE_VALUE == hStandardOutput)
    {
        err = ::GetLastError();
        std::cout << "GetStdHandle error code = " << err << std::endl;
        ::CloseHandle(hFile);
        return err;
    }
    //============================
    do
    {
        if (!::ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL))
        {
            err = ::GetLastError();
            std::cout << "ReadFile error code = " << err << std::endl;
            ::CloseHandle(hFile);
            return err;
        }
        if (bytesRead == 0) // EOF reached
            break;
        /*============= Works fine
        if (!::WriteFile(hStandardOutput, buffer, bytesRead, &bytesWritten, NULL))
        {
            err = ::GetLastError();
            std::cout << "WriteFile error code = " << err << std::endl;
            ::CloseHandle(hFile);
            return err;
        }
        */
        //------------- comment out when testing WriteFile 
        std::cout.write(buffer, bytesRead);
        //----------------------------------------
    }
    while (true);
    ::CloseHandle(hFile);
    return 0;
}