2013-10-07 48 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列,那么在输出阶段可以修正。

希望你发现这个方法有用。