从文本文件中读取数据。我需要做两件事,首先我需要读点数

时间:2013-05-28 19:32:57

标签: c++ armadillo

我的文本文件排列为:

Diam
<D>          4.2      
05:21:26 02:Apr:2012
Point 1
<X>       2   
<Y>       5
<Z>       6  
Point 2
<X>       4   
<Y>       2
<Z>       0
Point 3
<X>       4 
<Y>       1 
<Z>       2 
End
End

我需要做两件事,首先我需要读取点数,所以基本上读了多少行以Point开头,为此我写道:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    //count number of data points in file (only count lines starting with P)
    const char FileName[] = "myfile.txt";
    int num = 0.;
    string line;
    ifstream file;
    file.open (FileName);
    while(file.eof() == false)
    {
        getline(file, line);
        if (line[0]=='P')  num++;
    }
    file.close();

    return 0;
}

这给了我正确的点数(虽然欢迎改进代码),其次我需要读取x,y和z坐标并忽略其他所有内容。 不确定如何处理这个,有什么建议吗?

提前致谢!

编辑:

感谢大家的好主意和答案! Obvlious船长提供了一个很好的答案,我想知道它是否可以适应一点。

因此,原来.txt文件的格式为:

Diam
<D>          4.2      5    6    4   2
05:21:26 02:Apr:2012
Point 1
<X>       2   5    6    4   2
<Y>       5   4    4    8   3
<Z>       6   7    6    0   2
Point 2
<X>       4   2    6    4   2
<Y>       2   3    5    8   4
<Z>       0   7    6    3   2
Point 3
<X>       4   0    6    2   2
<Y>       1   5    6    7   4
<Z>       2   0    6    5   3
End
End

同样,我只需要第一列值,之后的所有其他内容都可以丢弃。 为简单起见,我使用int,但数字为double。 在阅读之后,我试图将值放在armadillo矩阵中,所以我的矩阵看起来像:

cout << matrix << endl;
2 5 6
4 2 0
4 1 2 

所以,我修改了Captain Obvlious'解决方案,将数据放在矩阵中:

#include <iostream>
#include <fstream>
#include <string>
#include <cctype>
#include <sstream>
#include <stdexcept>
#include <armadillo>

using namespace std;
using namespace arma;

int main()
{
    mat matrix(3,3);  //here is where I set the size of the matrix, 
                      //different files have different number of points,
                      //that's why I use my counting points code to set the size.
    const char FileName[] = "myfile.txt";
    string line;
    ifstream file;
    file.open (FileName);

    for(;;)
    {
        getline(file, line);
        if( file.eof() ) {
            break;
        }

        if (line.size() && line[0]=='P')
        {
            // Change "int" to "double" if necessary.
            struct { double  x, y, z; } data;

            for(int i = 0; i < 3; i++)
            {
                getline(file, line);
                if(line.size() > 3 && line[0] == '<' && line[2] == '>')
                {
                    string::value_type pointElement = line[1];

                    // skip spaces and process data here to get the value
                    string::size_type offset = line.find(' ');
                    if(string::npos == offset)
                    {
                        throw invalid_argument("Invalid data format");
                    }

                    stringstream sline(line.substr(offset));
                    int value;
                    if(!(sline >> value))
                    {
                        throw invalid_argument("invalid data format");
                    }


                    switch(pointElement)
                    {
                    case 'X':   data.x = value; break;
                    case 'Y':   data.y = value; break;
                    case 'Z':   data.z = value; break;
                    default:
                        // error in data format
                        throw invalid_argument("invalid data format");
                    }
                }
                else
                {
                    // error in data format
                    throw invalid_argument("invalid data format");
                }
            }

            // Do something with the values in data
            cout
                << "point[x=" << data.x
                << ", y=" << data.y
                << ", z=" << data.z
                << "]" << endl;
            // place data in matrix
            // need to loop over k where k is the row of the matrix
            matrix(k,0) = data.x; // format is matrix(rows,columns)
            matrix(k,1) = data.y;
            matrix(k,2) = data.z;
        }
    }
    file.close();

    return 0;
}

不确定将循环放在k上的位置设置矩阵的行以分配值。 对不起超长的帖子,并提前感谢您的帮助。在我看来,这是非常有用的东西,以供将来参考。

6 个答案:

答案 0 :(得分:2)

您需要单独处理每个点的数据行并将它们存储到某种类型的数据结构中,以便在加载X,Y和Z之后对它们进行处理。您还应该包含一些额外的验证代码,否则您可能会引入未定义的行为。有几种方法可以做到这一点,有些方法会让你重复代码,其他人会专注于在处理数据后分配数据。

下面的解决方案考虑了验证以及必须从3条单独的行读取数据的事实。它并不完美,并希望“myfile.txt”文件采用精确格式。它使用异常处理数据格式中的错误,并添加数据结构以在加载数据时保存数据。

#include <iostream>
#include <fstream>
#include <string>
#include <cctype>
#include <sstream>
#include <stdexcept>

using namespace std;

int main()
{
    const char FileName[] = "myfile.txt";
    string line;
    ifstream file;
    file.open (FileName);

    for(;;)
    {
        getline(file, line);
        if( file.eof() ) {
            break;
        } 

        if (line.size() && line[0]=='P')
        {
            // Change "int" to "double" if necessary.
            struct { int x, y, z; } data;

            for(int i = 0; i < 3; i++)
            {
                getline(file, line);
                if(line.size() > 3 && line[0] == '<' && line[2] == '>')
                {
                    string::value_type pointElement = line[1];

                    // skip spaces and process data here to get the value
                    string::size_type offset = line.find(' ');
                    if(string::npos == offset)
                    {
                        throw invalid_argument("Invalid data format");
                    }

                    stringstream sline(line.substr(offset));
                    int value;
                    if(!(sline >> value))
                    {
                        throw invalid_argument("invalid data format");
                    }


                    switch(pointElement)
                    {
                    case 'X':   data.x = value; break;
                    case 'Y':   data.y = value; break;
                    case 'Z':   data.z = value; break;
                    default:
                        // error in data format
                        throw invalid_argument("invalid data format");
                    }
                }
                else
                {
                    // error in data format
                    throw invalid_argument("invalid data format");
                }
            }

            // Do something with the values in data
            cout
                << "point[x=" << data.x
                << ", y=" << data.y
                << ", z=" << data.z
                << "]" << endl;
        }
    }
    file.close();

    return 0;
}

当对“myfile.txt”运行时,它会产生以下输出

  

点[x = 2,y = 5,z = 6]
  点[x = 4,y = 2,z = 0]
  点[x = 4,y = 1,z = 2]

答案 1 :(得分:1)

在你的while循环中,你可以随时添加如下内容:

if(line.size() > 3){
  if(line[0] == '<' && line[1] == 'X' && line[2] == '>'){
    //enter code here
  } else if(line[0] == '<' && line[1] == 'Y' && line[2] == '>'){
    //enter code here
  } else if(line[0] == '<' && line[1] == 'Z' && line[2] == '>'){
    //enter code here
  }
}

然后执行类似子行的操作并删除&lt; X> /&lt; Y> /&lt; Z>以及任何空格或特殊字符。

之后,您可以将剩余的字符串转换为您需要的任何内容

编辑: 一个稍微好一点的方法

  if(line.find("< X> ") != string::npos){
     //enter code here
  } else if(line.find("< Y> ") != string::npos){
     //enter code here
  } else if(line.find("< Z> ") != string::npos){
     //enter code here
  } 

答案 2 :(得分:1)

也许你想考虑使用ifstream / stringstreams让你的生活更轻松,虽然这不是最快的方式......

int pt[3];
string line,dummy;

... //to where you were
  if (line[0]=='P')
    {
        num++;
        for (int i =0; i<3; ++i)
        {
            getline(file,line);
            stringstream ss(line);
            ss>>dummy;
            ss>>pt[i];
            std::cout<<"got "<<pt[i]<<std::endl;
        }
    }

你还应该包括sstream和fstream

答案 3 :(得分:0)

这与S Grimminck的答案非常相似。

#include <iostream>
#include <string>
using namespace std;
int main()
{
const char FileName[] = "myfile.txt";

int num = 0;
int x = 0;
int y = 0;
int z = 0;
string line;
std::stringstream str_parse(line);
ifstream file;
file.open (FileName);

while(file.eof() == false)
{
    getline(file, line);

    if (line[0]=='P')  
        num++;
    if( line.size() > 3 )
    {
    if( (line[0] == '<') && (line[1] == 'X') && (line[2] == '>'))
    {
       line = line.substr(3,line.size());
       str_parse>>x; // Gets the x-coordinate
    } 
    else if((line[0] == '<') && (line[1] == 'Y') && (line[2] == '>'))
    {
       line = line.substr(3,line.size());
       str_parse>>y;  // Gets the y-coordinate
    } 
    else if((line[0] == '<') && (line[1] == 'Z') && (line[2] == '>'))
    {
       line = line.substr(3,line.size());
       str_parse>>z;  // Gets the z-coordinate
    }
    }
}
file.close();

return 0;
}    

这是我第一次使用stringstream。所以,我可能错了。随意纠正我。 :)

答案 4 :(得分:0)

你真的需要提前计算点数吗?为什么不直接对输入文件进行一次传递?

#include <iostream>
#include <fstream>
#include <iterator>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

struct Point {
    Point() {}
    Point(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {}

    int x, y, z;
};

istream& operator>>(istream& is, Point& p) {
    is >> ws;

    if (is.peek() != 'P') {
        is.setstate(ios::failbit);
        return is;
    }

    string pointStr;
    is >> pointStr;
    if (pointStr != "Point") {
        is.setstate(ios::failbit);
        return is;
    }

    int pointIdx;
    is >> pointIdx;

    string coordStr;
    int x, y, z;

    is >> coordStr;
    if (coordStr != "<X>") {
        is.setstate(ios::failbit);
        return is;
    }
    is >> x;

    is >> coordStr;
    if (coordStr != "<Y>") {
        is.setstate(ios::failbit);
        return is;
    }
    is >> y;

    is >> coordStr;
    if (coordStr != "<Z>") {
        is.setstate(ios::failbit);
        return is;
    }
    is >> z;

    p = Point(x, y, z);

    return is;
}

int main() {
    ifstream fileStream("data.txt");

    string diamStr;
    fileStream >> diamStr;
    if (diamStr != "Diam")
        throw istream::failure("Badly formatted file");

    string dStr;
    fileStream >> dStr;
    if (dStr != "<D>")
        throw istream::failure("Badly formatted file");
    double diam;
    fileStream >> diam;

    string time, date;
    fileStream >> time >> date;

    istream_iterator<Point> pointIt(fileStream);
    vector<Point> points;
    copy(pointIt, istream_iterator<Point>(), back_inserter(points));

    cout << points.size() << " points:\n";
    for_each(begin(points), end(points), [](const Point& p) {
        cout << "(" << p.x << ", " << p.y << ", " << p.z << ")\n";
    });
}

答案 5 :(得分:0)

你可以像这样创建一个坐标数组:int coords[num*3];

然后,使用模数函数将坐标数存储在各自的位置,并为x-coords找到pos mod 3 = 0的位置,y-coords的pos mod 3 = 1,z-coords的pos mod 3 = 2