C ++ stringstream以固定长度字符串读取到char数组

时间:2013-01-03 13:21:14

标签: c++ stringstream

如果数据格式为“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";
}

[输出] 0:1:2:3:4:5:CT_6,0:-45689,粗体部分显示stringstream无法读取4个字符仅用于dcontact。 dcontact实际上拥有4个以上的字符,为j留下了垃圾数据。

5 个答案:

答案 0 :(得分:1)

是的,N operator >> (istream&, char[N])没有特定的重载,char*也有>>,因此它认为是最佳匹配。 char *的重载读取到下一个空格字符,因此它不会停留在逗号处。

你可以将你的dcontact包装在一个结构中,并有一个特定的重载来读入你的结构。否则你可以使用read,虽然它打破了你可爱的ssline.read( dcontact, 4 ); 运营商链。

getline

将在那时工作。

要读取分隔符,顺便提一下,您可以使用get。 (getline也可以正常工作,std::stringget自由函数意味着您无需猜测长度。

(请注意,其他人已指定使用read而不是dcontact,但由于您dcontact末尾没有额外的字节,因此您的情况会失败如果你希望{{1}}以空值终止,那么使它成为5个字符并使用'get`并为你追加null。

答案 1 :(得分:1)

稍强一些(正确处理','分隔符):

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";
}

答案 2 :(得分:0)

试试这个

  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;
    }

答案 3 :(得分:0)

问题是字符串的>>运算符 (std::string或C样式字符串)实际上实现了 单词的语义,具有单词的特定定义。该 决定是任意的(我会把它作为一条线),但从那以后 一个字符串可以表示许多不同的东西,他们必须选择 东西。

通常,解决方案是不要在字符串上使用>>。 定义你想要的类(这里可能是类似的 Symbol),并为其定义一个尊重它的运算符>> 语义。你的代码将更加清晰,而你 可以根据需要添加各种invarant控件。如果你知道的话 那个字段总是正好四个字符,你可以做到 简单的事情:

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,虽然它只有三个足够大(因为 >>将插入终止'\0')。如果你再读一遍了 三个进入它,你有不确定的行为。

答案 4 :(得分:0)

由于字符串的长度已知,您可以使用std::setw(4),如

ssline >> std::setw(4) >> dcontact >> delimiter[6];