从理论上讲,这两个命令行应该等效:

1个

type tmp.txt | test.exe


2

test.exe < tmp.txt


我有一个涉及#1的过程,多年来一直运行良好;在去年的某个时候,我们开始使用Visual Studio的较新版本来编译程序,但由于输入格式错误,该程序现在失败了(请参见下文)。但是#2成功了(也不例外,我们看到了预期的输出)。为什么#2在#1失败的地方成功?

我已经能够将test.exe减少到下面的程序。我们的输入文件每行只有一个制表符,并且统一使用CR / LF行尾。因此,该程序永远不应写入stderr:

#include <iostream>
#include <string>

int __cdecl main(int argc, char** argv)
{
    std::istream* pIs = &std::cin;
    std::string line;

    int lines = 0;
    while (!(pIs->eof()))
    {
        if (!std::getline(*pIs, line))
        {
            break;
        }

        const char* pLine = line.c_str();
        int tabs = 0;
        while (pLine)
        {
            pLine = strchr(pLine, '\t');
            if (pLine)
            {
                // move past the tab
                pLine++;
                tabs++;
            }
        }

        if (tabs > 1)
        {
            std::cerr << "We lost a linebreak after " << lines << " good lines.\n";
            lines = -1;
        }

        lines++;
    }

    return 0;
}


当通过#1运行时,我得到以下输出,每次都具有相同的数字(在每种情况下,这是因为getline返回了两个串联的行,中间没有换行符);通过#2运行时,(正确)没有输出:

We lost a linebreak after 8977 good lines.
We lost a linebreak after 1468 good lines.
We lost a linebreak after 20985 good lines.
We lost a linebreak after 6982 good lines.
We lost a linebreak after 1150 good lines.
We lost a linebreak after 276 good lines.
We lost a linebreak after 12076 good lines.
We lost a linebreak after 2072 good lines.
We lost a linebreak after 4576 good lines.
We lost a linebreak after 401 good lines.
We lost a linebreak after 6428 good lines.
We lost a linebreak after 7228 good lines.
We lost a linebreak after 931 good lines.
We lost a linebreak after 1240 good lines.
We lost a linebreak after 2432 good lines.
We lost a linebreak after 553 good lines.
We lost a linebreak after 6550 good lines.
We lost a linebreak after 1591 good lines.
We lost a linebreak after 55 good lines.
We lost a linebreak after 2428 good lines.
We lost a linebreak after 1475 good lines.
We lost a linebreak after 3866 good lines.
We lost a linebreak after 3000 good lines.

最佳答案

原来是known issue


  该错误实际上存在于较低级别的_read函数中,即stdio
  库函数(包括fread和fgets)用于从
  文件描述符。
  
  _read中的错误如下:If…
  
  
  您正在从文本模式管道阅读,
  您调用_read读取N个字节,
  _read成功读取N个字节,并且
  读取的最后一个字节是回车符(CR),
  
  
  则_read函数将成功完成读取,但会
  返回N-1而不是N。在末尾的CR或LF字符
  结果缓冲区不计入返回值中。
  
  在此错误报告的特定问题中,fread调用_read进行填充
  流缓冲区。 _read报告它填充了N-1个字节
  缓冲区,最后的CR或LF字符丢失。
  
  该错误从根本上对时间敏感,因为_read是否可以
  从管道成功读取N个字节取决于有多少数据
  被写入管道。更改缓冲区大小或更改时间
  刷新缓冲区可以减少出现问题的可能性,但是它
  在100%的情况下不一定能解决此问题。
  
  有几种可能的解决方法:
  
  
  使用二进制管道并在阅读器端手动执行文本模式CRLF => LF翻译。这并不是特别困难
  (在缓冲区中扫描CRLF对;用一个LF替换它们)。
  使用_osfhnd(fh)调用ReadFile,完全绕过读取器端的CRT的I / O库(尽管这也需要手动
  文本模式翻译,因为操作系统不会为
  您)
  
  
  我们已修复此错误,以供下一次对通用CRT进行更新。注意
  通用CRT是操作系统组件,并且
  独立于Visual C ++库提供服务。下次更新
  到通用CRT的时间可能与
  今年夏天将Windows 10周年更新。

10-07 19:16
查看更多