2013-10-07 75 views
0

我試圖在數據文件中讀取的行的文件,大約2000線,該文件看起來像我怎樣才能讀取不同數量的數字

1.1 1.2 1.3 1.4 1.5 
1.6  1.7 1.8 1.9 
2.0 
2.1 2.2 2.3 2.4 2.5 

其實是有一個空白(白色空間)和1.3/1.7是在同一列

我把它設置爲存儲方式是結構的一個向量,其中

struct num 
{ 
    double d1, d2, d3, d4, d5; 
}; 

我所試圖實現的是

num A; 
vector<num> data 
for (int i = 0; i < 4; i++) 
{ 
    File >> A.d1 >> A.d2 >> A.d3 >> A.d4 >> A.d5; 
    data.push_back(A); 
} 

和查找來識別在第二行和存儲D1 = 1.6,D2 = 0,D3 = 1.7等的空白處是D1 = 2.0,D2,D3的邏輯..和第三線路,D4,D5 = 0 我如何測試/獲取邏輯實現這只是糊塗了,如果可能的話 我在C++ VS2010 看第一個回答後,認爲我應該提供更多的信息,文件中的每一行屬於一個衛星,每個數字代表在一個特定的波長的觀察,因此,如果它是空白這意味着它具有對波長沒有觀測。

所以闡述,第一行表示衛星1在所有5種波長的觀察,第2個reprsents satelittle 2,並且對波長上波長-1,3,4,5-和無觀測4.

這就是爲什麼我試圖將其作爲單獨的結構分解成每行,因爲每行都是單獨的衛星

+0

我假設用'2.0'行後有空白呢?那是8個? – jrd1

回答

0

爲什麼不使用std:vector來存放浮點數組。

要將新元素添加到您使用矢量:

std::vector::push_back

正如你在每一個字符讀,看,看它是否是一個數字或一個週期。

如果是,請將其添加到std::string,然後使用atofmystring.c_str()作爲參數將其轉換爲浮點型。

這也可以幫助字符串轉換爲浮動:

std::string to float or double

所以,讀入一個字符串,然後按下浮到一個矢量,重複,跳過所有不屬於數字或字符期。

在生產線的末端你的載體有所有的花車,如果你想將它們連接成一個自定義分隔符,你可以看看這個問題的答案的字符串:

std::vector to string with custom delimiter

+0

因爲我需要訪問我需要的信息的方式是分開的。每條線代表來自單個衛星的不同波長的5個觀測值,如果它是空白的,則意味着它沒有觀測到該波長。 – user2840470

+0

因此,如果有一個缺少的觀察,那麼只需要一個空元素,並且只需使用並浮點觀察[5],並確保在每個循環之前調用memset(以快速清理數組)。 –

2

觀察你的數據:

  • 每一個數據點存儲在以下模式:數據空間。
  • 如果數據點不存在,它是由一個空間中表示的,除非它是所有其他的輸出被截斷爲一個新行的最後一個不存在的數據點。

這是我想出了:

#include <fstream> 
#include <iostream> 
#include <string> 
#include <vector> 
#include <cstdlib> 
#include <sstream> 
#include <iomanip> 
#include <cctype> 
using namespace std; 

//note all the lines are stored WITH newlines at the end of them. 
//This is merely an artifact of the methodology I am using, 
//as the newline is a flag that truncates output (as per your problem) 
vector<string> preparse_input(const std::string& filename) { 
    vector<string> lines; 

    ifstream ifile; 

    ifile.open(filename.c_str(), ios::in); 
    if (!ifile.is_open()) { 
     exit(1); 
    } 

    string temp, chars, line; 
    char ch; 

    while(getline(ifile, temp)) { 
     temp += "\n";//getline removes the newline: because we need it, reinsert it 
     istringstream iss(temp); 

     //first read in the line char by char 
     while(iss >> noskipws >> ch) { 
      chars += ch; 
     } 

     bool replaced_newline = false; 
     int nargs = 0; 

     //I could have used iterators here, but IMO, this way is easier to read. Modify if need be. 
     for (int i = 0; i < chars.size(); ++i) { 
      if (isdigit(chars[i]) && chars[i+1] == ' ') { 
       nargs += 1; 
      } 
      else if(isspace(chars[i]) && isspace(chars[i+1])) { 
       if (chars[i+1] == '\n') { 
        replaced_newline = true; 
       } 
       //this means that there is no value set 
       //hence, set the value to 0 for the value part: 
       chars[i+1] = '0'; 
       line += chars[i]; 
       ++i;//now, skip to the next character since 1 is for spacing, the other is for the value 
       nargs += 1; 
      } 

      //now rebuild the line: 
      line += chars[i]; 

      if(isdigit(chars[i]) && chars[i+1] == '\n') { 
       nargs += 1; 
       //check nargs: 
       for (int i = nargs; i < 5; ++i) { 
        line += " 0"; 
        nargs += 1; 
       } 
      } 

      if (replaced_newline) { 
       line += '\n'; 
      } 
      replaced_newline = false; 
     } 

     lines.push_back(line); 
     chars.clear(); 
     line.clear(); 
    } 
    ifile.close(); 

    return lines; 
} 

//this way, it's much easier to adapt to any type of input that you may have 
template <typename T> 
vector< vector<T> > parse_input (const vector<string>& lines) { 
    vector< vector<T> > values; 
    T val = 0; 

    for(vector<string>::const_iterator it = lines.begin(); it != lines.end(); ++it) { 
     vector<T> line; 
     istringstream iss(*it); 
     string temp; 

     while(getline(iss, temp, ' ')) { 
      if (istringstream(temp) >> val) { 
       line.push_back(val); 
      } 
      else { 
       line.push_back(0);//this is the value that badly parsed values will be set to. 
          //you have the option of setting it to some sentinel value, say -1, so you can go back and correct it later on, if need be. Depending on how you want to treat this error - hard or soft (stop program execution vs adapt and continue parsing), then you can adapt it accordingly 
          //I opted to treat it as a soft error but without a sentinel value - so I set it to 0 (-1 as that is probably more applicable in a general case), and informed the user that an error occurred 
          //The flipside of that is that I could have treated this as a hard error and have `exit(2)` (or whatever error code you wish to set). 
       cerr << "There was a problem storing:\"" << temp << "\"\n"; 
      } 
     } 
     values.push_back(line); 
    } 
    return values; 
} 

int main() { 
    string filename = "data.dat"; 
    vector<string> lines = preparse_input(filename); 

    vector < vector<double> > values = parse_input<double>(lines); 

    for (int i = 0; i < values.size(); ++i) { 
     for (int j = 0; j < values[i].size(); ++j) { 
      cout << values[i][j] << " "; 
     } 
     cout << endl; 
    } 

    return 0; 
} 

綜上所述,我打破了字符串由字符閱讀每行的字符,然後用0更換空白用來分析重建的每一行。爲什麼?因爲沒有這樣的價值,就無法分辨哪個參數被存儲或跳過(使用默認的ifstream_object >> type方法)。

這樣,如果我然後使用stringstream對象來解析輸入我可以正確地確定哪個參數設置,或不設置;然後,存儲結果,一切都是華麗的。這是你的願望。

,並使用它的以下數據:

1.1 1.2 1.3 1.4 1.5 
1.6 1.7 1.8 1.9 
2.0   
2.0 
2.1 2.2 2.3 2.4 2.5 
2.1  2.4 

使你的輸出:

1.1 1.2 1.3 1.4 1.5 
1.6 0 1.7 1.8 1.9 
2 0 0 0 0 
2 0 0 0 0 
2.1 2.2 2.3 2.4 2.5 
2.1 0 0 2.4 0 

注:第3行具有8位(1沒有數據和1爲間隔)。第4行是您的原始數據。第6行包含5個空格(按照引用的模式)。

最後,讓我說,,這是迄今爲止,存儲的,我曾經遇到過數據的最瘋狂的方法之一。

+1

我很難找到更多瘋狂的格式,xml讓人想起,但人們並不傾向於同意我的觀點。 –

+0

我很欣賞這一點,它是一個巨大的幫助! 至於數據的存儲,我不知道任何其他方式來存儲它,並有效地訪問它,因爲每行代表1衛星,他們是2000行。您是否有任何關於如何存儲它以保持每條線彼此分開的建議 – user2840470

+0

@ user2840470:如果我的回覆對您有用,請考慮接受。除此之外,使用非空格分隔的數據格式比如csv會更好。這樣,如果前三列數據不見了,就不會有第二次猜測:',,, 4.1,2.3'。類似的東西。但是,這是另一回事。總體而言,僅使用C++爲您解析所有內容可能會有點過頭。理想情況下,您可以使用另一種與C++相結合的語言,比如Python,來幫助您進行數據預處理。 – jrd1

1

鑑於你的文件格式是分隔的,你可以使用正則表達式提取列的空間。我假定你可以使用C++ 11或者不使用Boost正則表達式。

然後你可以用下面的函數將字符串分割成令牌。

std::vector<std::string> split(const std::string& input, const std::regex& regex) { 
    // passing -1 as the submatch index parameter performs splitting 
    std::sregex_token_iterator 
     first(input.begin(), input.end(), regex, -1), 
     last; 
    return std::vector<std::string>(first, last); 
} 

舉個例子,假設你的數據在 「data.txt中」,我用這種方式獲得的值:

#include <iostream> 
#include <fstream> 
#include <string> 
#include <regex> 
#include <vector> 

using namespace std; 

std::vector<std::string> split(const string& input, const regex& regex) { 
    // passing -1 as the submatch index parameter performs splitting 
    std::sregex_token_iterator 
     first(input.begin(), input.end(), regex, -1), 
     last; 
    return vector<std::string>(first, last); 
} 

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

    string s; 
    while (getline(f, s)) 
    { 
     vector<string> values = split(s, regex("\\s")); 
     for (unsigned i = 0; i < values.size(); ++i) 
     { 
      cout << "[" << values[i] << "] "; 
     } 
     cout << endl; 
    } 

    return 0; 
} 

其中給出了以下結果:

[1.1] [1.2] [1.3] [1.4] [1.5] 
[1.6] [] [1.7] [1.8] [1.9] 
[2.0] [] [] [] 
[2.1] [2.2] [2.3] [2.4] [2.5] 

請注意,第4行缺少一列,但這是因爲我不太確定該行上有多少空格。如果知道最多隻有5列,那麼在輸出階段可以修正。

希望你發現這個方法有用。