c++ stringstream将固定长度的字符串读入char数组
C++ stringstream read in fixed length string to char array
给定数据格式为"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];