【问题标题】:Read newline using stream C++使用流 C++ 读取换行符
【发布时间】:2020-07-07 14:30:12
【问题描述】:

如何使用 C++ >> 运算符读取新行?

ifstream input("doc.txt".c_str());
vector<string> contents;
while (input >> word) {
    contents.push_back(word);
}

对于文件:

hello 
world
C++ is the best tool

应该返回

hello
\n
world
\n
C++
is
the
best
tool

P/S:这是一个大问题的简化问题。我解析文件的方式导致了这个问题。

【问题讨论】:

  • 这是 XY 问题吗?
  • 您的输出与您的输入不匹配。如您所见,文件中的 hello 和 world 之间有一个换行符。
  • operator&gt;&gt; doesn't discern between a newline and other whitespace 的字符串重载,除非有一个语言环境可以这样做,但 getline 可以。在任何情况下,打印 '\n' 而不是实际的换行符必须是一种特殊情况。您是否考虑过使用 getline 获取每一行,将生成的字符串转换为 istringstream ,在其上调用 operator&gt;&gt; ,然后在完全解析字符串流时在其自己的行上打印出 \n
  • 这正是我正在做的,但我正在争取更紧凑的代码......

标签: c++ stl


【解决方案1】:

您可以自己使用std::getlinepush_back"\n",正如jaggedSpire所提到的:

std::ifstream input("doc.txt");
std::vector<std::string> contents;

for (std::string line; std::getline(input, line);) {
    std::istringstream str(line);

    for (std::string word; str >> word;) {
        contents.push_back(word);
    }

    contents.push_back("\n");
}

【讨论】:

    【解决方案2】:

    如果您希望专门使用operator&gt;&gt;,并且在技术上不需要专门使用字符串,您可以简单地创建一个自定义类,当它从istream 读入时具有您想要的行为。它甚至可以(主要)是字符串的包装器,在读取初始空白时具有自定义行为。

    class StringAndNewline{
        std::string str_;
        friend std::istream& operator>>(std::istream& in, StringAndNewline& str);
    public:
        StringAndNewline() : str_(){}
        StringAndNewline(std::string str) : str_(str){}
    
        const std::string& str() const noexcept {return str_;}
        std::string release() {return std::move(str_);}
    };
    

    读入操作符的字符串自动忽略所有preceding whitespace 到由当前语言环境定义的非空白字符序列。这是您想要改变的行为,事实证明这样做非常简单。

    初始空白的处理通常由称为哨兵对象的东西执行,该对象还检查流是否有效并设置流的failbit(如果它位于文件末尾)。虽然它的默认行为是在遇到非空白字符之前使用空格,但这是由其构造函数中的标志控制的,因此我们可以使用它提供的非常好的封装流有效性检查。

    operator&gt;&gt; 的字符串重载生成并检查哨兵,然后读取直到遇到空格、流结束或读取失败。我们可以简单地通过自己处理它来确保它的哨兵永远不会遇到空白。

    因此,我们自定义类的自定义operator&gt;&gt; 的最终读入结构将如下所示:

    • 制作非空白吃哨兵
    • 检查哨兵,如果无效则返回失败的流
    • 处理空白
    • 将数据读入包装字符串
    • 返回流

    由于我们只关心空格中的 '\n' 字符,这也很简单:只需在流有效时循环(如果它在满足我们的任何一个条件之前用完空间,它将设置 failbit 就像如果两个条件之一是净值,则退出循环:我们得到一个换行符,或者我们得到一个非空白字符。同样,令人愉快的简单:

    std::istream& operator>>(std::istream& in, StringAndNewline& str){
        std::istream::sentry sentry{in, true}; // make a sentry that doesn't eat whitespace
        if(!sentry){return in;} // check the sentry
        std::locale
            presentLocale{}; // get the present locale
        char presentChar;
        while(in.get(presentChar)){ // while the stream is valid
            if(presentChar == '\n'){ // if we get a newline
                str.str_ = "\\n"; // set the string to an escaped newline
                break; // exit the loop
            }
            // if we get a non-whitespace character
            else if(!std::isspace(presentChar, presentLocale)){
                in.unget(); // replace the character in the stream
                in >> str.str_; // take advantage of the existing string operator
                break; // done with loop
            }
        }
        return in; // return the istream, whatever state it might be in
    }
    

    完成后,我们设置一个 ostream 操作符以便于打印:

    std::ostream& operator<<(std::ostream& out, const StringAndNewline& str){
        return out << str.str();
    }
    

    并测试我们的代码:

    int main (){
        std::istringstream file(
            "hello\n"
            "world\n"
            "C++ is the best tool"
        );
        StringAndNewline
            wordOrNewline;
        while(file >> wordOrNewline){
            std::cout << wordOrNewline << '\n';
        }
    }
    

    打印这个:

    hello
    \n
    world
    \n
    C++
    is
    the
    best
    tool
    

    就像我们想要的一样! Live on Coliru

    如果您真的想轻松地将包装类转换为字符串,您甚至可以编写一个字符串运算符,但我将把它留给您。

    【讨论】:

      【解决方案3】:

      尝试使用 getline (http://www.cplusplus.com/reference/istream/istream/getline/)。 getline 将遍历每一行(直到它看到换行符)并在到达文件末尾时返回 0。因此,在每次调用 getline 并打印它之后,也会打印 \n 。这是您的问题的示例,randFile 是一个随机文件,其中包含文本。

        1 #include <iostream>
        2 #include <fstream>
        3 int main(){
        4 
        5   std::ifstream myFile("randFile", std::ifstream::in);
        6   char s[BUFSIZ];
        7 
        8   while(myFile.getline(s, BUFSIZ)){
        9     std::cout << s << std::endl;
       10     std::cout << "\\n"<< std::endl;
       11   }
       12 
       13   return 0;
       14 }
      

      【讨论】:

        【解决方案4】:

        首先,您已经在流的构造函数中作为 const char * 传递。 其次,流阅读器读取的是字符而不是空格,这就是它知道何时切入字符串的方式。

        通常我们读取一个二进制文件,有一个读者知道的字符告诉我们当我们跳过一行时著名的 \n 但它与平台(Win,Unix)不同。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-21
          相关资源
          最近更新 更多