使用getline从输入文件中通过重定向命令“ <”从stdin读取的字符串未正确附加

时间:2018-08-20 19:48:24

标签: c++ string stdin io-redirection

问题陈述

我想将字符串“太棒了!” 附加到另一个使用getline函数从stdin中读取的字符串“冰淇淋” 中。

因此产生“冰淇淋很棒!”

如果输入是手动键入的,则附加操作将完美地工作。

但是,如果输入是通过重定向命令“ <” 从文件中获取的,则添加操作的行为就不会相似。

因此产生的非常棒!冰淇淋

代码

让我通过代码演示问题。

#include<iostream>

int main()
{
    // err : will be read from stdin using getline
    // err : is expected to fail in appending
    std::string err;

    // ok  : will be modified internally
    // ok  : is expected to succeed in appending
    std::string ok = "Pancakes";

    std::cout<<"Before executing  std::getline..."<<std::endl;
    std::cout<<"err \t:\t"<<err<<std::endl;
    std::cout<<"ok \t:\t"<<ok<<std::endl;
    std::cout<<"-------------------"<<std::endl;

    // we now use getline to read in string value for err
    // stdin can have any string , let's assume it is "Ice creams"
    std::cout<<"-------------------"<<std::endl;
    std::cout<<"Please enter string manually or < from file "<<std::endl;
    std::getline(std::cin,err);
    std::cout<<"-------------------"<<std::endl;


    std::cout<<"After executing std::getline..."<<std::endl;
    std::cout<<"err \t:\t"<<err<<std::endl;
    std::cout<<"ok \t:\t"<<ok<<std::endl;
    std::cout<<"-------------------"<<std::endl;

    //-------------------------------------------//
    //               THE PROBLEM                 //
    //-------------------------------------------//
    // we try to append to err
    err += " are awesome !";
    // we try to append to ok
    ok  += " are awesome !";

    std::cout<<"After executing append operation..."<<std::endl;
    std::cout<<"Where,the '+=' operator is used..."<<std::endl;
    std::cout<<"err \t:\t"<<err<<std::endl;
    std::cout<<"ok \t:\t"<<ok<<std::endl;
    std::cout<<"-------------------"<<std::endl;


    return 0;
}

端子输出

借助WSL功能在Windows 10计算机上运行的g ++进行编译。

C:\test>wsl g++ test.cpp -o a.out -std=c++11

C:\test>wsl ./a.out
Before executing  std::getline...
err     :
ok      :       Pancakes
-------------------
-------------------
Please enter string manually or < from file
Ice creams
-------------------
After executing std::getline...
err     :       Ice creams
ok      :       Pancakes
-------------------
After executing append operation...
Where,the '+=' operator is used...
err     :       Ice creams are awesome !
ok      :       Pancakes are awesome !
-------------------

C:\test>wsl ./a.out < in-test.in
Before executing  std::getline...
err     :
ok      :       Pancakes
-------------------
-------------------
Please enter string manually or < from file
-------------------
After executing std::getline...
err     :       Ice creams
ok      :       Pancakes
-------------------
After executing append operation...
Where,the '+=' operator is used...
 are awesome !  Ice creams
ok      :       Pancakes are awesome !
-------------------

C:\test>

结果

  • 通过标准输入进行手动输入,Append会产生正确的结果。
  • 对于从标准输入中的文件 in-test.in 输入的内容,Append产生乱码。

注意: in-test.in 文件包含一行,字符串为“ Ice creams”。

注意:如果很难发现该错误,请允许我指出。

这行很棒!冰淇淋位于终端输出的末尾。

问题

  • 为什么会这样?
  • 如何避免呢?

2 个答案:

答案 0 :(得分:3)

请注意,该行甚至不包含应该以{{1​​}}开头的输出,但是"err \t:\t"的输出却显示了应该到达的位置?

那可能是因为输入文件中包含前导回车符err

答案 1 :(得分:0)

知识

Some programmer dude很好,提到字符串中可以包含'\r'

问题源

可以理解,Linux,Macintosh和Windows具有不同的行终止符,因此当我们希望将文件从一个系统传输到另一个系统并期望它们正常工作时,可能会遇到一些问题。

这个问题的解释很优美:Difference between CR LF, LF and CR line break types?

这就是发生的事情。我使用Notepad.exe创建了文件,因此自动为它分配了CRLF行终止符。但是,由于我使用Windows Subsystem for Linux (WSL)(基本上是在Windows 10内运行的Ubuntu)编译程序,因此创建的a.out是Linux可执行文件。

将带有CRLF行终止符的文件重定向到可执行文件stdin中会导致问题。

使用MinGW或MSYS2后,迁移到WSL的用户可能会遇到此特定问题。

自此以来,MinGW和MSYS2可以很好地与CRLF编码配合使用,而WSL则不能。

修复

只需使用名为 unix2dosdos2unix 的工具更改行终止符即可。

顾名思义,它们会将文本文件转换为与dos和unix兼容 基于操作系统。

  • 在使用通过Linux / WSL创建的文件以使其与Windows可执行文件兼容时,请使用 unix2dos
  • 使用Windows创建的文件使其与Linux / WSL可执行文件兼容时,请使用 dos2unix

演示

C:\test>wsl g++ test.cpp -o a.out -std=c++11

C:\test>wsl unix2dos in-test.in
unix2dos: converting file in-test.in to DOS format ...

C:\test>wsl file in-test.in
in-test.in: ASCII text, with CRLF line terminators

C:\test>wsl ./a.out < in-test.in
Before executing  std::getline...
err     :
ok      :       Pancakes
-------------------
-------------------
Please enter string manually or < from file
-------------------
After executing std::getline...
err     :       Ice creams
ok      :       Pancakes
-------------------
After executing append operation...
Where,the '=' operator overloading is used...
 are awesome !  Ice creams
ok      :       Pancakes are awesome !
-------------------

C:\test>wsl dos2unix in-test.in
dos2unix: converting file in-test.in to Unix format ...

C:\test>wsl file in-test.in
in-test.in: ASCII text

C:\test>wsl ./a.out < in-test.in
Before executing  std::getline...
err     :
ok      :       Pancakes
-------------------
-------------------
Please enter string manually or < from file
-------------------
After executing std::getline...
err     :       Ice creams
ok      :       Pancakes
-------------------
After executing append operation...
Where,the '=' operator overloading is used...
err     :       Ice creams are awesome !
ok      :       Pancakes are awesome !
-------------------

结果

  • 由WSL创建的可执行文件最初不适用于带有CRLF行终止符的输入文件,然后

  • 使用工具 dos2unix ,将文件的行终止符更改为与Linux兼容,从而与WSL兼容,从而使程序成功运行。因此,此修复程序有效。