c++ stringstream将固定长度的字符串读入char数组

C++ stringstream read in fixed length string to char array

本文关键字:字符串 char 数组 stringstream c++      更新时间:2023-10-16

给定数据格式为"int,int,…",int,string,int",是否可以使用stringstream(仅)来正确解码字段?

(代码)

int main(int c, char** v)
{
    std::string line = "0,1,2,3,4,5,CT_O,6";
    char delimiter[7];
    int id, ag, lid, cid, fid, did, j = -12345;
    char dcontact[4]; // <- The size of <string-field> is known and fixed
    std::stringstream ssline(line);
    ssline >> id >> delimiter[0]
    >> ag >> delimiter[1]
    >> lid >> delimiter[2]
    >> cid >> delimiter[3]
    >> fid >> delimiter[4]
    >> did >> delimiter[5]  // <- should I do something here?
    >> dcontact >> delimiter[6]
    >> j;
    std::cout << id << ":" << ag << ":" << lid << ":" << cid << ":" << fid << ":" << did << ":";
    std::cout << dcontact << "n";
}

[Output] 0:1:2:3:4:5:CT_6,0:-45689,粗体部分显示stringstream未能读取到dcontact的4个字符。

dcontact实际上保存了超过4个字符,j留下了垃圾数据。

是的,对于N没有特定的operator >> (istream&, char[N])过载,而对于char*则有,因此它将其视为最佳匹配。char*的重载会读取到下一个空白字符,因此它不会在逗号处停止。

你可以把你的dcontact包装在一个结构体中,并有一个特定的重载来读入你的结构体。否则你可以使用read,尽管它打破了你可爱的>>操作符链。

ssline.read( dcontact, 4 );

将在此点起作用。

顺便说一下,要读取到分隔符,可以使用getline。(get也可以工作,但getline自由函数写入std::string将意味着您不必猜测长度)。

(注意,其他人已经指定使用get而不是read,但这在您的情况下将失败,因为您在dcontact数组的末尾没有额外的字节用于空终止符。

如果你想让dcontact以null结尾,那么让它成为5个字符并使用'get ', null将为你追加。

稍微健壮一点(正确处理','分隔符):

template <char D>
std::istream& delim(std::istream& in)
{
  char c;
  if (in >> c && c != D) in.setstate(std::ios_base::failbit);
  return in;
}
int main()
{
  std::string line = "0,1,2,3,4,5,CT_O,6";
  int id, ag, lid, cid, fid, did, j = -12345;
  char dcontact[5]; // <- The size of <string-field> is known and fixed
  std::stringstream ssline(line);
  (ssline >> id >> delim<','>
          >> ag >> delim<','>
          >> lid >> delim<','>
          >> cid >> delim<','>
          >> fid >> delim<','>
          >> did >> delim<','> >> std::ws
          ).get(dcontact, 5, ',') >> delim<','>
          >> j;
  std::cout << id << ":" << ag << ":" << lid << ":"
            << cid << ":" << fid << ":" << did << ":";
            << dcontact << "n";
}

问题是>>运算符用于字符串(std::string或C风格字符串)实际上实现了一个词的语义,具有特定的词的定义。的决定是武断的(我会把它画成一条线),但既然一个字符串可以表示许多不同的东西,他们必须选择什么。

一般来说,解决方案是永远不要在字符串上使用>>。定义您想要的类(在这里,可能类似于Symbol),并为其定义一个运算符>>语义。你的代码会更清晰,而且你可以酌情添加各种不变控件。如果你知道字段总是正好四个字符,你可以做到比如:
class DContactSymbol
{
    char myName[ 4 ];
public:
    //  ...
    friend std::istream&
    operator>>( std::istream& source, DContactSymbol& dest );
    //  ...
};
std::istream&
operator>>( std::istream& source, DContactSymbol& dest )
{
    std::sentry guard( source );
    if ( source ) {
        std::string tmp;
        std::streambuf* sb = source.rdbuf();
        int ch = sb->sgetc();
        while ( source && (isalnum( ch ) || ch == '_') ) {
            tmp += static_cast< char >( ch );
            if ( tmp.size() > sizeof( dest.myName ) ) {
                source.setstate( std::ios_base::failbit );
            }
        }
        if ( ch == source::traits_type::eof() ) {
            source.setstate( std::ios_base::eofbit );
        }
        if ( tmp.size() != sizeof( dest.myName ) ) {
            source.setstate( std::ios_base::failbit );
        }
        if ( source ) {
            tmp.copy( dest.myName, sizeof( dest.myName ) );
        }
    }
    return source;
}

(请注意,与其他一些建议不同,例如使用std::istream::read,它保持了所有通常的约定,如跳过前导空白依赖于skipws国旗。)

当然,如果你不能100%保证符号会总是4个字符,你应该使用std::string,和修改相应的>>操作符

顺便说一句,你似乎想读四个字符dcontact,虽然它只够三个人使用>>将插入终止'')。如果你再读下去的话

try this

  int main(int c, char** v) {
    string line = "0,1,2,3,4,5,CT_O,6";
    char delimiter[7];
    int id, ag, lid, cid, fid, did, j = -12345;
    char dcontact[5]; // <- The size of <string-field> is known and fixed
    stringstream ssline(line);
    ssline >> id >> delimiter[0]
            >> ag >> delimiter[1]
            >> lid >> delimiter[2]
            >> cid >> delimiter[3]
            >> fid >> delimiter[4]
            >> did >> delimiter[5];
    ssline.get(dcontact, 5);
    ssline >> delimiter[6]
            >> j;
    std::cout << id << ":" << ag << ":" << lid << ":" << cid << ":" << fid << ":" << did << ":";
    std::cout << dcontact << "n" << j;
    }

因为字符串的长度是已知的,所以可以使用std::setw(4),如

ssline >> std::setw(4) >> dcontact >> delimiter[6];
相关文章: