【问题标题】:Simple C++ input from file...how to?来自文件的简单 C++ 输入...如何?
【发布时间】:2010-02-22 07:30:52
【问题描述】:

我有一个文件:

P 0.5 0.6 0.3
30 300
80 150
160 400
200 150
250 300
T
r  45 0 0
s 0.5 1.5 0 0
t 200 –150
.
.
.

当我读到“P”时,我知道接下来会有 3 个花车。接下来是有限数量的 X 和 Y 坐标。这个数字会有所不同,直到达到我必须识别的“T”。然后可能会有一个 'r'、's' 或 't' 后跟一些值。

无论如何,我知道如何识别“P”,然后接收 2 个浮点数,但后来我知道我必须有一个用于 X 和 Y 坐标的 while 循环,当我到达“T”时它将停止。我对 C++ 的了解不够,无法让循环停止并识别“T”然后做其他事情。

一个例子来解释将不胜感激。提前致谢!

【问题讨论】:

标签: c++ file-io while-loop conditional-statements


【解决方案1】:

我将向您展示我认为正确的 C++ 方法。首先定义一个类来表示您的第一行并执行其 IO:

struct FirstLine
{
    double x, y, z;
    friend std::istream & operator>>(std::istream & is, FirstLine & data)
    {
        std::string line, ignore;
        std::getline(is, line);
        std::istringstream iss(line);
        iss >> ignore >> data.x >> data.y >> data.z;
        assert(ignore == "P" && iss);
        return is;
    }
    friend std::ostream & operator<<(std::ostream & os, FirstLine const & data)
    {
        return os << "P " << data.x << " " << data.y << " " << data.z;
    }    
};

我已经用 assert 添加了一些基本的错误检查,你可能希望在你的最终程序中更健壮。

现在是中间线类:

struct MiddleLine
{
    double x, y;
    friend std::istream & operator>>(std::istream & is, MiddleLine & data)
    {
        std::string line;
        std::getline(is, line);
        if(line == "T")
            is.clear(std::ios::failbit);
        else
        {
            int n = sscanf(line.c_str(), "%lf %lf", &data.x, &data.y);
            assert(n == 2);
        }
        return is;
    }
    friend std::ostream & operator<<(std::ostream & os, MiddleLine const & data)
    {
        return os << data.x << " " << data.y;
    }    
};

当我们到达中间线所在部分的末尾时,我们应该会遇到一个“T”。在这种情况下,我们会提高流的失败位,这将告诉客户端没有更多的中间行要读取。

最后几行的类:

struct LastLine
{
    std::string identifier; // r, s or t
    std::vector<double> values;
    friend std::istream & operator>>(std::istream & is, LastLine & data)
    {
        std::string line;
        std::getline(is, line);
        std::istringstream iss(line);
        iss >> data.identifier;
        assert(data.identifier == "r" || data.identifier == "s" 
               || data.identifier == "t");
        std::copy(std::istream_iterator<double>(iss), 
                  std::istream_iterator<double>(), std::back_inserter(data.values));
        return is;
    }
    friend std::ostream & operator<<(std::ostream & os, LastLine const & data)
    {
        os << data.identifier << " ";
        std::copy(data.values.begin(), data.values.end(),
                  std::ostream_iterator<double>(os, " "));
        return os;
    }      
};

最后几行比较复杂,因为我们不知道每行有多少个值,所以我们尽可能多地读取。

那是棘手的部分。现在我们的 main 函数将简单地读取第一行,然后是未知数量的中间行,最后是未知数量的最后一行:

int main()
{
    std::string const data = "P 0.5 0.6 0.3\n
                             "30 300\n"
                             "80 150\n"
                             "160 400\n"
                             "200 150\n"
                             "250 300\n"
                             "T\n"
                             "r  45 0 0\n"
                             "s 0.5 1.5 0 0\n"
                             "t 200 –150";

    std::istringstream iss(data);

    FirstLine first_line;
    iss >> first_line;

    std::vector<MiddleLine> middle_lines;
    std::copy(std::istream_iterator<MiddleLine>(iss), 
              std::istream_iterator<MiddleLine>(), 
              std::back_inserter(middle_lines));
    iss.clear();

    std::vector<LastLine> last_lines;
    std::copy(std::istream_iterator<LastLine>(iss), 
              std::istream_iterator<LastLine>(), 
              std::back_inserter(last_lines));
    assert(iss.eof());       

    std::cout << first_line << "\n";
    std::copy(middle_lines.begin(), middle_lines.end(),
              std::ostream_iterator<MiddleLine>(std::cout, "\n"));
    std::copy(last_lines.begin(), last_lines.end(),
              std::ostream_iterator<LastLine>(std::cout, "\n"));
    return 0;
}

这是你将得到的输出::

P 0.5 0.6 0.3
30 300
80 150
160 400
200 150
250 300
r 45 0 0
s 45 0 0 0.5 1.5 0 0
t 45 0 0 0.5 1.5 0 0 200

我使用字符串作为数据源,但您可能希望从文件中读取。

仅此而已,您可以看到我没有写一个循环。

Here's the code 在键盘中。

【讨论】:

  • 很好地展示了如何使用op&gt;&gt; 做到这一点。几个问题:1) 为什么sscanf 换成MiddleLine? 2) 为什么getline 一次一行变成istringstream 而不是直接使用istream 参数? 3) failbit 是要设置的正确位吗? istream_iterator 一直到流结束,那么为什么不 eofbit
  • 1) 我认为这样会不那么冗长 2) 混合 getline 和格式化操作 (op>>) 会迫使您一直使用 clear+ignore,这很痛苦. 3)这不是流的真正结束,只是中间线部分的“逻辑”结束。但 EOF 本来可以奏效的,这是真的。
  • 感觉有点矫枉过正,不是吗?
  • @gineer - 获得正确的 IO 很棘手,我发现像这样的结构化方法从长远来看可以节省时间。除了我只使用最基本的IO设施,我不知道它怎么会矫枉过正。 IMO 会在 ANTLR 或 Boost.Spirit 中编写解析器。
  • 感谢std::copy(std::ostream_iterator,...)的成语,以前没见过,用得着!
【解决方案2】:
  • 保持一种“全局状态”
  • 编写一个循环,从文件中读取一行直到文件结束。
  • 将行读入缓冲区
  • 检查缓冲区的第一个字符,如果是 P 或 T 或 r 或 s 或 t,则更改应用程序的全局状态
  • 如果第一个字符是 T,则使用 sscanf(Buffer+1,"%lf %lf %lf",&first,&second,&third) 读取该行的其余部分。
  • 如果第一个字符是 r、s 或 t,则执行类似操作。
  • 如果应用程序处于“P 状态”,只需使用 sscanf(Buffer,"%lf %lf",&first,&second) 扫描缓冲区

【讨论】:

    【解决方案3】:

    用户逐行读取文本文件,然后 运行字符串word;

    ifstream fin(your file)
    while(! fin.eof())
    {
        string line = fin.getline();
        istringstream iss(line, istringstream::in);
        string token;
        while( iss >> token)     
        {
          if (token.compare("T")) {
            ...
          } else {
            float f = atof(token.c_str());
         }
        }
    }
    

    【讨论】:

      【解决方案4】:

      我认为您可以使用标准流
      检查“P”和“T”
      使用 get(char &ch);
      并 putback(ch) 将其推回流

      你的流 >> x >> y >> endl;

      http://www.cplusplus.com/reference/iostream/istream/putback/

      // Example
      // istream putback
      #include <iostream>
      using namespace std;
      
      int main () {  
        char c;  
        int n;  
        char str[256];  
      
        cout << "Enter a number or a word: ";
        c = cin.get();  
      
        if ( (c >= '0') && (c <= '9') )
        {  
          cin.putback (c);
          cin >> n;
          cout << "You have entered number " << n << endl;
        }  
        else
        {  
          cin.putback (c);
          cin >> str;
          cout << " You have entered word " << str << endl;
        }  
      
        return 0;  
      }
      

      【讨论】:

      • 为什么不使用cin.peek() 而不是get() 后跟putback()
      猜你喜欢
      • 2013-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-23
      • 1970-01-01
      • 1970-01-01
      • 2016-08-16
      • 1970-01-01
      相关资源
      最近更新 更多