2013-05-28 55 views
2

的號碼,我有一個文本文件排列爲:從文本文件中讀取數據。我需要做兩件事情,第一我需要讀點

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 

我需要做兩件事情,第一我需要閱讀的點數,所以基本上都看過多少線,點開始,爲了這個,我寫道:

#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 

所以,我修改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設置爲矩陣的行來分配值。 對不起,對於超長篇帖子,並提前感謝您的任何幫助。在我看來,這是非常有用的東西,以備將來參考。

+1

你可以使用awk嗎?它會簡化整個問題 – Nebril

+1

@Nebril我不能:( – user1981855

+0

)你顯然很熟悉使用'std :: getline()'(對於那個很好,btw)。跟隨你的點檢查有什麼問題嗎?三個'std :: getline()'調用,然後將這些行(一次一個)送入一個'istringstream'並提取標籤和值,填充一個點元組,並將它推入一個向量?(和注意:你會發現使用'file.eof()'作爲循環終止符不會像你想象的那樣工作)。 – WhozCraig

回答

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]

+0

我看到這個唯一的問題是任何數字超過1位數字將不會被使用。切換到stringstream將是一個更好的主意 –

+0

@SGrimminck其實它只檢查第一個字符,以確保它是一個數字..真正的問題是它不適用於負數。我已更新爲使用字符串流並正確檢查eof。 –

+0

@CaptainObvlious此解決方案似乎工作,我添加#包括與invalid_argument一起工作。另外,你如何使它適應雙打而不是整數? – user1981855

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 
    } 
} 

然後像做串線,並刪除< X>/< Y>/<ž>和任何空格或特殊字符。

後,你可以轉換爲任何你需要

編輯其餘字符串: 一個稍微好一點的方式

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

只要確保首先檢查字符串的長度。 –

+0

對,我忘了。 –

0

這是很相似的小號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。所以,我可能是錯的。隨時糾正我。 :)

+2

sscanf不是C++的方式:)從字符串流中讀取一個int – Jerome

+0

即時在這裏引用Obvlious上校,記得在做所有檢查之前檢查一下行長。 –

+0

@Srimminck:我同意。忘了那個。 :) – Josh

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的

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

取決於應用程序,他可能需要知道它是什麼點。這個我能想到的最大的用途是網格。你需要知道三角形構成什麼點,因爲它可能不是順序的。 –

+0

如果索引不能保證是連續的,你可以讀取它們並存儲它們,但是除非格式允許其他非點數據與點交錯,否則你只需要一次傳遞該文件。 – mattnewport

0

可以創建一個座標陣列是這樣的:int coords[num*3];

然後,存儲使用模函數和尋找具有正模3 = 0的位置用於x COORDS在各自的位置的座標數,POS y座標的mod 3 = 1,z座標的pos mod 3 = 2。

相關問題