2016-04-21 3 views
2

В теории, эти две команды-линии должны быть эквивалентны:Почему перенаправление работает там, где трубопроводы терпят неудачу?

type tmp.txt | test.exe 
test.exe < tmp.txt 

У меня есть процесс, включающий # 1, что на протяжении многих лет, работал просто отлично ; в какой-то момент в течение прошлого года мы начали компилировать программу с более новой версией Visual Studio, и теперь она терпит неудачу из-за неправильного ввода (см. ниже). Но №2 успешно (без исключения, и мы видим ожидаемый результат). Почему # 2 будет успешным, если № 1 не удастся?

Я смог уменьшить test.exe к нижеуказанной программе. Наш входной файл имеет ровно одну вкладку на строку и равномерно использует окончания строки CR/LF. Так эта программа никогда не должны использовать потоки:

#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. 
+0

Я думаю, что это считается минимальным и полным. Чтобы получить возможность проверки, мне нужно будет разместить точный входной файл, но я не вижу способ прикреплять файлы здесь. –

+0

вы могли бы предоставить исходный код для программы, которая генерирует входной файл, например, [вот попытка продемонстрировать ту же проблему в Python] (https://gist.github.com/zed/dd44ade13d313ceb8ba8e384ba1ff1ac) – jfs

ответ

3

Это оказывается быть known issue:

Это ошибка в том, в функции _read ниже уровня, который из stdio.h Функции библиотеки (включая как fread, так и fgets) используются для чтения из файлового дескриптора .

Исправлена ​​ошибка в _read выглядит следующим образом: Если ...

  1. вы читаете из текстового режима трубы,
  2. вы звоните _read читать N байт,
  3. _read успешно считывает N байт, а
  4. последний байт чтения является возврат каретки (CR) характер,

то функция _read завершит успешно прочитан, но будет вернуть N-1 вместо N. Символ CR или LF в конце буфера результатов не учитывается в возвращаемом значении.

В конкретном выпуске, указанном в этой ошибке, fread вызывает _read для заполнения буфера потока. _read сообщает, что он заполнил N-1 байты буфера , и окончательный символ CR или LF будет потерян.

Исправлена ​​ошибка, связанная с ошибкой _read может успешно читать N байтов из трубы зависит от того, сколько данных имеет , которые были записаны в трубу. Изменение размера буфера или изменение, когда буфер сброшен, может уменьшить вероятность проблемы, но не обязательно обойдутся проблемой в 100% случаев.

Есть несколько возможных путей их устранения:

  1. использование бинарной трубы и сделать текстовый режим CRLF => LF перевод вручную на стороне считывателя. Это не особенно сложно сделать (сканирование буфера для пар CRLF, заменить их на один LF).
  2. вызова ReadFile с _osfhnd (ФХ), минуя библиотеку ввода/вывода ЭЛТ на стороне читателя полностью (хотя это также требует ручного режима текста перевода, так как операционная система не будет делать перевод в текстовом режиме для вы)

Мы исправили эту ошибку для следующего обновления Universal CRT. Примечание , что универсальная ЭЛТ является компонентом операционной системы и обслуживается независимо от библиотек Visual C++. Следующее обновление к Универсальному ЭЛТ, вероятно, будет примерно в тот же промежуток времени, что и Обновление для Windows 10 Anniversary этим летом.

Смежные вопросы