2012-10-06 84 views
1

我有一个包含整数和特殊含义字符'#'的输入流。它看起来如下: ... 12 18 16 # 22 24 26 15 # 17 # 32 35 33 ... 令牌由空格分隔。 '#'的位置没有模式。根据类型标记字符串流

我试图来标记输入流是这样的:

int value; 
std::ifstream input("data"); 
if (input.good()) { 
    string line; 
    while(getline(data, line) != EOF) { 
    if (!line.empty()) { 
     sstream ss(line); 
     while (ss >> value) { 
     //process value ... 

     } 
    } 
    } 
} 

这段代码的问题是,处理遇到的第一个“#”时停止。

我能想到的唯一解决方案是将每个单独的标记提取为一个字符串(不是'#')并使用atoi()函数将字符串转换为整数。但是,由于大多数令牌都是整数,所以效率很低。在令牌上调用atoi()会带来很大的开销。

有没有一种方法可以根据其类型解析个人令牌?即整数,解析为整数,而'#',跳过它。谢谢!

+1

使用函数getline两次acccepable?如果是这样,使用'getline(data,line,'#');'fisrt。 – andre

+0

@ahenderson我没有明白你的观点。 getline的一个函数签名是:istream&getline(istream&is,string&str,char delim);通过将'#'作为第三个参数传递给getline,getline()将以'#'作为分隔符。 – itnovice

+0

我发布了一个答案。 – andre

回答

2

一种可能性是显式跳过空格(ss >> std::ws),然后使用ss.peek()来确定是否跟随#。如果是,则使用ss.get()来读取并继续,否则使用ss >> value读取该值。

如果#的位置不重要,也可以在将其初始化为stringstream之前从行中删除所有'#'

+0

谢谢。您的解决方案总是展望未来,'#'使用ss.get()来消耗它并继续前进。如果'#'可以是其他非数字字符,我们可以有更通用的解决方案吗? – itnovice

+0

@itnovice:如果你的数字都是正整数(即只包含数字“0”到“9”),你可以将它传递给'isdigit'。如果是数字,则可以继续阅读该值,否则您知道它是非数字字符。或者,您可以使用测试'next''0'|| next>'9'(其中'next'包含'peek'的结果)来标识非数字。 – celtschk

0

就个人而言,如果你的分隔符是总是不管接下来是什么,我建议你只需要输入字符串并从那里解析。这样,你可以拿起字符串,看看它是一个数字还是#和什么。

1

也许可以读取所有值的std :: string,然后检查它是否是“#”或没有(如果没有 - 转换为int)

0

我认为你应该重新审视你的前提是“呼唤atoi()上的令牌引入了大量的开销 - “

std::cin >> val没有什么神奇。在引擎盖下,它最终召唤(与atoi非常相似)。

如果您的代币很大,创建std::string可能会有一些开销,但正如您所说,绝大多数是数字(其余都是#),所以它们大多应该是短的。

+0

我不知道std :: cin >> val最终调用了一些类似于atoi()的函数。所以我的假设是错误的。感谢您指出了这一点。 – itnovice

1
int value; 
std::ifstream input("data"); 
if (input.good()) { 
    string line; 
    std::sstream ss(std::stringstream::in | std::stringstream::out); 
    std::sstream ss2(std::stringstream::in | std::stringstream::out); 
    while(getline(data, line, '#') { 
     ss << line; 
     while(getline(ss, line, ' ') { 
      ss2 << line; 
      ss2 >> value 
      //process values ... 
      ss2.str(""); 
     } 
     ss.str(""); 
    } 
} 

在这里,我们首先在第一拆分令牌“#”行while循环然后在第二个while循环,我们通过“”分割线。

+0

我在这台机器上没有C++编译器,代码没有测试。所以把它作为它应该如何工作的大纲。 – andre

+0

我明白你的观点。这是一个聪明的解决方案。谢谢 – itnovice

+0

@itnovice小错误我有'ss >>值'应该是ss2 >>值 – andre

2

通常不值得对测试好()

if (input.good()) { 

除非你的下一个操作是生成错误消息或异常。如果不好,所有进一步的操作都会失败。

不要针对EOF进行测试。

while(getline(data,line)!= EOF){

std :: getline()的结果不是整数。它是对输入流的引用。输入流可转换为布尔类似的对象,可用于布尔上下文中(如whileif等..)。所以你想做什么:

while(getline(data, line)) { 

我不知道我会读一行。你可以只读一个词(因为输入是空格分隔的)。在串

std::string word; 
while(data >> word) { // reads one space separated word 

使用>>运算符现在可以测试这个词,看它是否是你的特殊字符:

if (word[0] == "#") 

如果没有的话转换成一个数字。

这是我会做什么:

// define a class that will read either value from a stream 
class MyValue 
{ 
    public: 
    bool isSpec() const {return isSpecial;} 
    int value() const {return intValue;} 

    friend std::istream& operator>>(std::istream& stream, MyValue& data) 
    { 
     std::string item; 
     stream >> item; 
     if (item[0] == '#') { 
      data.isSpecial = true; 
     } else 
     { data.isSpecial = false; 
      data.intValue = atoi(&item[0]); 
     } 
     return stream; 
    } 
    private: 
    bool isSpecial; 
    int intValue; 
}; 

// Now your loop becomes: 
MyValue val; 
while(file >> val) 
{ 
    if (val.isSpec()) { /* Special processing */ } 
    else    { /* We have an integer */ } 
} 
+0

您的解决方案是非常好的。谢谢! – itnovice