2013-01-03 161 views
0

给定数据格式为“int,int,...,int,string,int”,是否可以使用stringstream(only)来正确解码字段?C++ 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,粗体部分表示字符串流未能读取4炭只有dcontact。 dcontact实际上保持超过4个字符,留下垃圾数据j

+0

http://stackoverflow.com/questions/53849/how-do-i-tokenize-a-string-in -c – user93353

回答

1

是的,没有特定的过载N和char*,所以它认为这是最好的匹配。 char *的重载读取到下一个空白字符,因此它不会停在逗号处。

你可以将你的dcontact包装在一个结构体中,并有特定的重载读入你的结构体。否则,你可以使用阅读,尽管它打破了你的可爱链条>>运营商。

ssline.read(dcontact, 4); 

将在该点工作。

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

(请注意,其他人已经指定使用get而非read,但是这会在你的情况下,失败,因为你没有在你dcontact阵列的空终止的最后一个额外的字节。如果你想dcontact到以空字符结束,然后使其成为5个字符并使用“get”,空字符将被附加给你)。

+0

感谢您的信息,我还搜索了有关stringstream.read() –

+0

的参考资料。由于字符串的长度已知,因此您可以使用'std :: setw(4)',如'ssline >> std :: setw(4)>> dcontact;' – bames53

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

如果你使用get,你需要编辑dcontact使它成为5个字符,否则会溢出。 get为你添加一个空终止符。阅读不。 – CashCow

+0

谢谢,编辑。 – Khaledvic

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

需要使用操纵器来处理分隔符,但请注意,'istream :: get'不会跳过空白区域,而不考虑'skipws'标志。不同于'>>',它用于其他所有内容。所以如果它有'“5,CT_0,6”',输入将会失败。 –

+0

@JamesKanze你是对的,我忘了'ws'(固定)。 – ipc

+0

+1因为你记得修复dcontact为5个字符来使用get。当然,你认为它是一个字符串,因此有这样的终结者。 – CashCow

0

的问题是,>>运营商的字符串 (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,虽然它只有足够大的三(因为 >>将插入一个终止'\0')。如果你再读了三个以上的 ,你就有未定义的行为。

+0

您可以安全地将4个字符读入dcontact,如果您将它视为以空字符结尾的字符串,它将只是一个问题,因为它不是。 read()会将流中接下来的4个字节放入dcontact中,如果流中有许多剩余的话。你是对的,它不会跳过空白或解释分隔符。这只是一个“哑巴”的副本。有时候,这就是你想要的 - 因为它只是给你那里的东西,而不是试图弄清楚你想要的东西。 假设你的程序写入文件,你应该知道它里面有什么。 – CashCow

+0

这是一个文本文件。你不能假设任何事情。即使你的程序写了它,从那时起,有人可以编辑它。而且由于他可能需要其他类型的类,因此定义一个'>>'似乎是最自然的解决方案。 –

0

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

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