【问题标题】:std::getline alternative when input line endings are mixed混合输入行结尾时的 std::getline 替代
【发布时间】:2011-07-14 14:19:31
【问题描述】:

我正在尝试从std::istream 中读取行,但输入可能包含'\r' 和/或 '\n',因此std::getline 没有用。

很抱歉,但这似乎需要强调......

输入可能包含任一换行类型或两者都

有没有标准的方法来做到这一点?目前我正在尝试

char c;
while (in >> c && '\n' != c && '\r' != c)
    out .push_back (c);

...但这会跳过空格。哦! std::noskipws -- 更多需要摆弄,现在它很糟糕。

肯定有更好的方法吗?!?

【问题讨论】:

  • 分隔符是混合在一个文件中,还是只是在文件之间有所不同?
  • 我知道你可以通过 1 次完成,但我会做 2 次,一次将所有行尾(CR、LF、CRLF)更改为 std::endl(使用 @987654328 @ 读取字符而不是提取运算符),然后在第二遍使用getline

标签: c++ stdin stdio istream line-endings


【解决方案1】:

好的,这是一种方法。基本上我已经实现了std::getline,它接受谓词而不是字符。这让你有 2/3 的路程:

template <class Ch, class Tr, class A, class Pred>
std::basic_istream<Ch, Tr> &getline(std::basic_istream<Ch, Tr> &is, std::basic_string<Ch, Tr, A>& str, Pred p) {

    typename std::string::size_type nread = 0;      
    if(typename std::istream::sentry(is, true)) {
        std::streambuf *sbuf = is.rdbuf();
        str.clear();

        while (nread < str.max_size()) {
            int c1 = sbuf->sbumpc();
            if (Tr::eq_int_type(c1, Tr::eof())) {
                is.setstate(std::istream::eofbit);
                break;
            } else {
                ++nread;
                const Ch ch = Tr::to_char_type(c1);
                if (!p(ch)) {
                    str.push_back(ch);
                } else {
                    break;
                }
            }
        }
    }

    if (nread == 0 || nread >= str.max_size()) {
        is.setstate(std::istream::failbit);
    }

    return is;
}

使用类似这样的函子:

struct is_newline {
    bool operator()(char ch) const {
        return ch == '\n' || ch == '\r';
    }
};

现在,剩下的唯一事情就是确定您是否以'\r' 结尾...,如果您这样做了,那么如果下一个字符是'\n',只需使用它并忽略它。

编辑:所以要将这一切都放入一个功能性解决方案中,下面是一个示例:

#include <string>
#include <sstream>
#include <iostream>

namespace util {

    struct is_newline { 
        bool operator()(char ch) {
            ch_ = ch;
            return ch_ == '\n' || ch_ == '\r';
        }

        char ch_;
    };

    template <class Ch, class Tr, class A, class Pred>
        std::basic_istream<Ch, Tr> &getline(std::basic_istream<Ch, Tr> &is, std::basic_string<Ch, Tr, A>& str, Pred &p) {

        typename std::string::size_type nread = 0;

        if(typename std::istream::sentry(is, true)) {
            std::streambuf *const sbuf = is.rdbuf();
                str.clear();

            while (nread < str.max_size()) {
                int c1 = sbuf->sbumpc();
                if (Tr::eq_int_type(c1, Tr::eof())) {
                    is.setstate(std::istream::eofbit);
                    break;
                } else {
                    ++nread;
                    const Ch ch = Tr::to_char_type(c1);
                    if (!p(ch)) {
                        str.push_back(ch);
                    } else {
                        break;
                    }
                }
            }
        }

        if (nread == 0 || nread >= str.max_size()) {
            is.setstate(std::istream::failbit);
        }

        return is;
    }
}

int main() {

    std::stringstream ss("this\ris a\ntest\r\nyay");
    std::string       item;
    util::is_newline  is_newline;

    while(util::getline(ss, item, is_newline)) {
        if(is_newline.ch_ == '\r' && ss.peek() == '\n') {
            ss.ignore(1);
        }

        std::cout << '[' << item << ']' << std::endl;
    }
}

我对原始示例进行了一些小改动。 Pred p 参数现在是一个引用,以便谓词可以存储一些数据(特别是最后一个测试的char)。同样,我创建了谓词 operator() 非常量,以便它可以存储该字符。

主要是,我在std::stringstream 中有一个字符串,其中包含所有 3 个版本的换行符。我使用我的util::getline,如果谓词对象说最后一个char'\r',那么我在前面peek() 并忽略1 字符,如果它恰好是'\n'

【讨论】:

  • 谢谢,感谢您的努力。我惊呆了,没有一个著名的单线!
【解决方案2】:

读取一行的常用方法是使用std::getline

编辑:如果您的 std::getline 实现被破坏,您可以编写自己的类似内容,如下所示:

std::istream &getline(std::istream &is, std::string &s) { 
    char ch;

    s.clear();

    while (is.get(ch) && ch != '\n' && ch != '\r')
        s += ch;
    return is;
}

我应该补充一点,从技术上讲,这可能不是 std::getline 被破坏的问题,因为底层流实现被破坏 - 由流来转换表示行尾的任何字符平台,变成换行符。然而,无论哪些部分被破坏,如果你的实现被破坏,这可能能够弥补它(同样,如果你的实现被破坏得足够严重,也很难确定它是否会起作用)。

【讨论】:

  • 不。 getline 无法处理不明确的分隔符。
  • 在我看来,该实现知道特定平台上的换行符(即标准库函数在 Mac、PC 或 Linux 上运行良好),否则会令人抓狂。
  • 阅读问题:输入可能包含 either 换行符类型。
  • @spraff 谢谢你的澄清。
  • @spraff:我读过这个问题——但我也了解流(应该)是如何工作的。公平地说,它认为行尾是实现定义的,但是如果您在一个平台上,应该将 '\r' 或 '\n' 视为行尾,那么任何一个都应该被读作换行符(在文本/翻译模式下)。
猜你喜欢
  • 1970-01-01
  • 2013-11-09
  • 2015-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-06
  • 1970-01-01
  • 2012-11-12
相关资源
最近更新 更多