首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何安全地读取std::istream中的行?

如何安全地读取std::istream中的行?
EN

Stack Overflow用户
提问于 2013-12-18 05:49:54
回答 3查看 21.2K关注 0票数 53

我想安全地读取std::istream中的一行。流可以是任何东西,例如,Web服务器上的连接或处理未知来源提交的文件的内容。有许多答案开始与这段代码的道德等价物:

代码语言:javascript
复制
void read(std::istream& in) {
    std::string line;
    if (std::getline(in, line)) {
        // process the line
    }
}

考虑到in的来源可能可疑,使用上面的代码会导致漏洞:恶意代理可能会使用很长的一行代码对此代码发起拒绝服务攻击。因此,我希望将行长度限制为某个相当高的值,比如400万char秒。虽然可能会遇到一些大的行,但为每个文件分配一个缓冲区并使用std::istream::getline()是不可行的。

如何才能限制行的最大大小,理想情况下不会严重扭曲代码,也不会预先分配大量内存?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-12-18 06:13:08

您可以使用最大字符数读取参数编写您自己的std::getline版本,类似于getline_n之类的东西。

代码语言:javascript
复制
#include <string>
#include <iostream>

template<typename CharT, typename Traits, typename Alloc>
auto getline_n(std::basic_istream<CharT, Traits>& in, std::basic_string<CharT, Traits, Alloc>& str, std::streamsize n) -> decltype(in) {
    std::ios_base::iostate state = std::ios_base::goodbit;
    bool extracted = false;
    const typename std::basic_istream<CharT, Traits>::sentry s(in, true);
    if(s) {
        try {
            str.erase();
            typename Traits::int_type ch = in.rdbuf()->sgetc();
            for(; ; ch = in.rdbuf()->snextc()) {
                if(Traits::eq_int_type(ch, Traits::eof())) {
                    // eof spotted, quit
                    state |= std::ios_base::eofbit;
                    break;
                }
                else if(str.size() == n) {
                    // maximum number of characters met, quit
                    extracted = true;
                    in.rdbuf()->sbumpc();
                    break;
                }
                else if(str.max_size() <= str.size()) {
                    // string too big
                    state |= std::ios_base::failbit;
                    break;
                }
                else {
                    // character valid
                    str += Traits::to_char_type(ch);
                    extracted = true;
                }
            }
        }
        catch(...) {
            in.setstate(std::ios_base::badbit);
        }
    }

    if(!extracted) {
        state |= std::ios_base::failbit;
    }

    in.setstate(state);
    return in;
}

int main() {
    std::string s;
    getline_n(std::cin, s, 10); // maximum of 10 characters
    std::cout << s << '\n';
}

不过,这可能有点过头了。

票数 37
EN

Stack Overflow用户

发布于 2013-12-18 06:56:02

已经有这样一个getline函数作为istream的成员函数,您只需要包装它以进行缓冲区管理。

代码语言:javascript
复制
#include <assert.h>
#include <istream>
#include <stddef.h>         // ptrdiff_t
#include <string>           // std::string, std::char_traits

typedef ptrdiff_t Size;

namespace my {
    using std::istream;
    using std::string;
    using std::char_traits;

    istream& getline(
        istream& stream, string& s, Size const buf_size, char const delimiter = '\n'
        )
    {
        s.resize( buf_size );  assert( s.size() > 1 );
        stream.getline( &s[0], buf_size, delimiter );
        if( !stream.fail() )
        {
            Size const n = char_traits<char>::length( &s[0] );
            s.resize( n );      // Downsizing.
        }
        return stream;
    }
}  // namespace my
票数 18
EN

Stack Overflow用户

发布于 2013-12-18 06:52:26

通过在std::istream::getline周围创建包装器来替换std::getline

代码语言:javascript
复制
std::istream& my::getline( std::istream& is, std::streamsize n, std::string& str, char delim )
    {
    try
       {
       str.resize(n);
       is.getline(&str[0],n,delim);
       str.resize(is.gcount());
       return is;
       }
    catch(...) { str.resize(0); throw; }
    }

如果您想要避免过多的临时内存分配,您可以使用一个循环来根据需要增加分配(每次遍历时可能会加倍大小)。不要忘记,异常可能会在istream对象上启用,也可能不会启用。

下面是一个具有更有效的分配策略的版本:

代码语言:javascript
复制
std::istream& my::getline( std::istream& is, std::streamsize n, std::string& str, char delim )
    {
    std::streamsize base=0;
    do {
       try
          {
          is.clear();
          std::streamsize chunk=std::min(n-base,std::max(static_cast<std::streamsize>(2),base));
          if ( chunk == 0 ) break;
          str.resize(base+chunk);
          is.getline(&str[base],chunk,delim);
          }
       catch( std::ios_base::failure ) { if ( !is.gcount () ) str.resize(0), throw; }
       base += is.gcount();
       } while ( is.fail() && is.gcount() );
    str.resize(base);
    return is;
    }
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20645396

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档