【问题标题】:Parsing only numbers from istream in C++在 C++ 中仅解析来自 istream 的数字
【发布时间】:2011-10-30 08:40:30
【问题描述】:

我有一堆如下所示的输入文件:

(8,7,15)
(0,0,1) (0,3,2) (0,6,3)
(1,0,4) (1,1,5)

我需要编写一个函数,一次解析这些输入一个数字,所以我需要能够按数字分隔输入,例如:8,然后是 7,然后是 15,然后是 0,另一个是 0,依此类推.

到目前为止,我想到的唯一方法是使用 istream.get(),它返回下一个字符的 ASCII 码,我可以通过将其转换为字符来将其转换回其字符格式。然后我会检查该字符是否为数字(因此忽略括号),但是这样,任何双(或三)位数字一次只能读取一个数字。

实现这一目标的最佳方法是什么?

顺便说一句,我必须使用 istream。这是规范的一部分,我不允许更改

谢谢

【问题讨论】:

  • 一次读取一个字符的两位数或三位数有什么问题吗?您所要做的就是将目前读取的数字乘以 10,然后加上下一位数字的值。把它放在一个循环中,你就完成了。
  • 谢谢 john,这基本上是手动的方式,我希望在 STL 中的某个地方存在一些可以帮助我做得更好的东西!
  • 好吧,您可以使用 istream::unget 将最后读取的字符返回到字符串中。这样你就可以取消第一个数字,然后使用>>。但坦率地说,手动方式是不错的方式。
  • 或者,由于您的输入看起来很规则,您可以将标点符号读入虚拟变量。像in >> lparen >> num1 >> comma1 >> num2 >> comma2 >> num3 >> rparen; 这样的东西,其中 lparen 等被声明为 char。但是这样的代码很脆弱,我会手动做。

标签: c++ parsing input istream


【解决方案1】:

这是一种解决方案:

struct integer_only: std::ctype<char> 
{
    integer_only(): std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table()
    {
        static std::vector<std::ctype_base::mask> 
            rc(std::ctype<char>::table_size,std::ctype_base::space);

        std::fill(&rc['0'], &rc['9'+1], std::ctype_base::digit);
        return &rc[0];
    }
};

int main() {
        std::cin.imbue(std::locale(std::locale(), new integer_only()));
        std::istream_iterator<int> begin(std::cin);
        std::istream_iterator<int> end;
        std::vector<int> vints(begin, end);
        std::copy(vints.begin(), vints.end(), std::ostream_iterator<int>(std::cout, "\n"));
        return 0;
}

输入:

(8,7,15)
(0,0,1) (0,3,2) (0,6,3)
(1,0,4) (1,1,5)

输出:

8 7 15 0 0 1 0 3 2 0 6 3 1 0 4 1 1 5 

在线演示:http://ideone.com/Lwx9y

在上面,你必须在成功打开文件后将std::cin替换为文件流,如:

 std::ifstream file("file.txt");
 file.imbue(std::locale(std::locale(), new integer_only()));
 std::istream_iterator<int> begin(file);
 std::istream_iterator<int> end;
 std::vector<int> vints(begin, end); //container of integers!

这里,vints 是一个包含所有整数的向量。您希望与vints 一起做一些有用的事情。此外,您可以在预期 int* 的地方使用它:

void f(int *integers, size_t count) {}

f(&vints[0], vints.size()); //call a function which expects `int*`.

仅从文件中读取单词时可以应用类似的技巧。这是一个例子:

【讨论】:

  • 该代码更改了流,因此它将所有非数字视为空格!我不确定我是否印象深刻。对我来说似乎有点快和肮脏。
  • 谢谢!很快就会测试出来。顺便说一句,文件被读取为标准输入,所以不需要打开文件:)
  • 其实我觉得我同意 john 的观点,我不确定我是否喜欢改变流的想法,并且必须声明一个结构来完成这个相对简单的任务,似乎有点与约翰的方法相比,太过分了!
  • @Arvin:为流提供自己的语言环境有什么问题?如果不允许程序员更改语言环境,为什么首先存在函数imbue()?另外,我想知道问题是否如此simple task,那你为什么首先问它? :-/
  • @Nawaz:一个简单的问题。您可以调整上面的代码来处理带符号的数字吗?大概您会将“+”和“-”作为 ctype_base::punct 添加到您的表中。还是 istream 代码会自动处理签名?
【解决方案2】:

这里有一些代码,您可以根据自己的精确需求进行调整

for (;;)
{
  int ch = in.get();
  if (ch == EOF)
    break;
  if (isdigit(ch))
  {
    int val = ch - '0';
    for (;;)
    {
      ch = in.get();
      if (!isdigit(ch))
        break;
      val *= 10;
      val += ch - '0';
    }
    // do something with val
  }
}

这是未经测试的代码。

【讨论】:

  • 谢谢约翰!这段代码看起来比 Nawaz 的解决方案好一点,我想我更喜欢这个,但我必须测试两个版本才能下定决心
  • 其实我觉得他们很像。他们都基本上忽略了标点符号。您必须做出的决定是您是否要尝试阅读和验证标点符号。这显然更复杂。我发布上面的代码是因为我认为您不知道如何通过一次读取一个字符来计算整数值。但我现在可以看出我错了。
【解决方案3】:

尝试读取一个数字。如果失败,请清除错误状态并尝试读取char(并忽略它)。重复这两个步骤,直到读取一个字符失败,在这种情况下,您处于 EOF 或真正的失败。

它可以通过识别')'然后读取到'('来优化。

但我认为这不值得。

干杯,

【讨论】:

    【解决方案4】:

    另一种解决方案:

    #include <string>
    #include <ostream>
    #include <fstream>
    #include <iostream>
    
    struct triple
    {
        long a;
        long b;
        long c;
    };
    
    std::ostream& operator << (std::ostream& os, const triple& value)
    {
        return os << value.a << "/" << value.b << "/" << value.c;
    }
    
    int main()
    {
        std::ifstream stream("Test.txt");
        if (!stream)
        {
            std::cout << "could not open the file" << std::endl;
        }
    
        std::string dummy;
        triple value;
        while (std::getline(stream, dummy, '(') >> value.a &&
               std::getline(stream, dummy, ',') >> value.b &&
               std::getline(stream, dummy, ',') >> value.c)
        {
            std::cout << value << std::endl;
        }
    }
    

    【讨论】:

      【解决方案5】:
      int getFirstPos(const string& str)
      
      {
      int pos=0,PosHit=0;
      bool bfind=false;
      if((PosHit=str.find(','))!=string::npos){
          if(!bfind)  pos=PosHit;
          pos=pos>PosHit?PosHit:pos;
          bfind=true;
      }
      if((PosHit=str.find('('))!=string::npos){
          if(!bfind)  pos=PosHit;
          pos=pos>PosHit?PosHit:pos;
          bfind=true;
      }
      if((PosHit=str.find(')'))!=string::npos){
          if(!bfind)  pos=PosHit;
          pos=pos>PosHit?PosHit:pos;
          bfind=true;
      }
      return bfind?pos:string::npos;
      

      }

      void main()
      
      {
          ifstream ifile("C:\\iStream.txt");
          string strLine;
          vector<double> vecValue;    //store the datas
          while(getline(ifile,strLine)){
              if(strLine.size()==0)
                  continue;
              int iPos=0;
              while((iPos=getFirstPos(strLine))!=string::npos)
                  strLine[iPos]=' ';
              istringstream iStream(strLine);
              double dValue=0;
              while(iStream>>dValue)
                  vecValue.push_back(dValue);
          }
          //output the result!
          vector<double>::iterator it;
          for (it=vecValue.begin(); it!=vecValue.end()  ; ++it){
              cout<<setprecision(3)<<*it<<endl;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多