【问题标题】:File size is larger than it should, extra new lines are added文件大小大于应有的大小,添加了额外的新行
【发布时间】:2017-03-18 18:51:50
【问题描述】:

简介:

我正在阅读带有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)
    {
        // '\0' terminate buffer, needed for cout only
        ::memset(buffer, '\0', sizeof(buffer)); 

        if (!::ReadFile(hFile, 
            buffer, 
            sizeof(buffer) - 1, // - 1 for '\0', 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,但我不确定。

【问题讨论】:

  • 这与文件完全无关。只有std::cout &lt;&lt; 的工作原理
  • @RbMm:你能解释一下,或者提供一个链接来解释为什么会发生这种情况吗?感谢您的评论(我没有忘记您的其他 IO 完成端口答案,我正在解决这个问题)。
  • cout 不是为处理原始二进制数据而设计的。绝对正常,他可以添加额外的\r\n 字符。您将文件读取为原始二进制数据,但尝试写入格式化的字符串数据。结果不匹配也就不足为奇了。在这种情况下尝试使用 WriteFile 并进行比较。或确定你想要什么
  • cout 不是为处理原始二进制数据而设计的 在 MVCE 中,我尝试使用 cout.write,但也失败了……try use WriteFile 是的,那工作,正如我已经在我的帖子中所说的那样。感谢您的回复。
  • 新文件比原来大多少?我们是在说一些额外的字节吗?两倍大?这些类型的细节很重要。

标签: c++ file winapi


【解决方案1】:

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

Binary and text modes

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

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

没有标准方法可以在二进制模式下使用std::cout,因此\n 输出为\n 而不是\r\n。但是,请参阅How to make cout behave as in binary mode?,了解一些可能的方式,您可以在 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;
}

【讨论】:

  • 感谢您回答我的问题。这里已经很晚了,所以我想睡觉。我设法测试了您的代码,它显示的行为与我的 OP 中的行为相同。看来我将不得不坚持使用WriteFile。在与WriteFile 达成和解之前,我会“战斗”一段时间。再次感谢您。
  • 我决定使用WriteFile,谢谢你的帮助。
猜你喜欢
  • 2013-04-13
  • 2016-02-16
  • 2017-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多