首页 > 解决方案 > Asigning variables for individual elements in a row

问题描述

i'm a quite unexperienced c++ user and i've recently encountered a problem. I need to write a script that would operate on given input. Input is a coordinate file including 108 rows with 4 records in each row. First record is always a letter, specifying the element and the other three records are three numbers of equivalent length, representing x y and z components, with blank space fs, like this:

O      4.972407     6.001956     6.766559
O     -1.537917     5.179561     5.602830
.....

My script should extract certain rows and print them in some output file. This i've already done using while, if and getline, like this:

if (myfile.is_open())
{
   int line_no = 1;  
   while (line_no <= 108 && getline(myfile,line)) {
     line_no++;  
     if (line_no == 19) {
       getline (myfile,line);  
       cout << line << '\n';  
     }
     else if (line_no == 59) {
      getline (myfile,line);  
      cout << line << '\n';  
     }
     else if (line_no == 60) {
      getline (myfile,line);  
      cout << line << '\n';  
     }
     else if (line_no == 87) {
       getline (myfile,line);  
       cout << line << '\n';  
     }
     else if (line_no == 102) {  
       getline (myfile,line);  
       cout << line << '\n';   
     }
  }
  myfile.close();
}

The lines extracted have to be in precise order, so i've had to include 17 of those with 17 line strings. It get pretty extensive and i'm sure there should be an easier way to do this. I would appreciate any suggestions and ideas.

Now the real problem comes when i actually get the output file. I need to assign a variable for each number in a row, in order to put them inside a formula. I tried doing this using array, like this:

const int SIZE = 60;  
double grades[SIZE];  

void readData() {
  string inFileName = "out.txt";  
  ifstream inFile;   
  inFile.open(inFileName.c_str());  

  if (inFile.is_open())
  {   
     for (int i = 0; i < SIZE; i++)  
     {  
        inFile >> grades[i];  
        cout << grades[i] << " ";  
     }  

     inFile.close(); // CLose input file  
  }  
  else { //Error message   
    cerr << "Can't find input file " << inFileName << endl;  
  }
}  

but i think there must be a problem with every fourth element being a letter. The output is get:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

I'd appreciate any ideas, corrections, suggestions that would improve my script.


[edit]

About the first part. I have a coordinate file and i need to extract specific lines in sets of five, like this:

P     -3.070974     5.319084     5.505385   
O     -3.375664     6.157090     4.244211  
O      4.972407     6.001956     6.766559  
O     -1.537917     5.179561     5.602830   
O      4.900104     3.921648     5.407940  
P      3.070979     4.630705     1.835121   
O      3.644369     3.947834     0.573947 ....

I cannot do it using just one while cycle cause it will print out lines according to their initial position. So, likewise i wrote 17 while loops like the one i showed to print out 17 sets. Any suggestions?

Input file looks like this:

P      6.783213     2.487446     1.835121   
P     -2.474827     4.974898    20.186419  
P      2.474819     4.974898     9.175649   
P      1.237407     7.118158    20.186419  
P     -1.237416     2.831639     9.175649  
P      1.237407     2.831639    20.186419   
P     -1.237416     7.118158     9.175649   
P      5.545802     0.344186    12.845891    
P      5.545802     4.630705    12.845891   
P      1.833567     2.487446    12.845891    
P      2.474823     0.000000    16.516155    
P      3.070974     5.319084    16.516155   
P     -1.237412     2.143260    16.516155     
O     -0.664021     1.460388    15.254981   
O      4.972412     1.027057    14.107065    
O      3.375664     6.157090    15.254981     
O      5.241113     3.792699    14.107065     
O     -2.711642     7.307209    15.254981     
O      2.711646     2.642580    14.107065    
O      1.596740     5.130033    10.436823     
O     -1.596748     4.819764    18.925245     
O     -0.664026     3.514510    10.436823     
O      0.664017     6.435287    18.925245      

and on and on.

I know the atoms i need, that means i know the line i need to extract, thats all. So all i wanted to ask, is there any way to improve my script?

标签: c++

解决方案


关于你的第二部分:

 double grades[SIZE];  
 ...
 for (int i = 0; i < SIZE; i++)  
 {  
     inFile >> grades[i];  
     cout << grades[i] << " ";  
 }  

因为

第一条记录总是一个字母

第一个inFile >> grades[i];不会成功(像“O”这样的字母不是有效的浮点表示)并且inFiLe将出错,下一个>>也不会做任何事情。

所以成绩没有在for中设置,你总是打印来自全局数组初始化的默认值 0。

如果你想记住双打,你还必须阅读这封信,所以要阅读一个字母(可能通过一个字符串)然后 3 double 然后再读一个字母等

我鼓励您检查每次读取是否成功以检测无效文件,包括过早的 EOF

例如:

#include <iostream>
#include <fstream>

using namespace std;

const int SIZE = 60;  
double grades[SIZE];  

void readData() {
  string inFileName = "out.txt";  
  ifstream inFile;   
  inFile.open(inFileName.c_str());  // only old C++ version does not accept std::string and required the c_str

  if (inFile.is_open())
  {   
    string s; // to read 2 letters like Ti in your input after edition

    for (int i = 0; i < SIZE; i++)  
    {  
      if ((i % 3) == 0) {
        if (! (inFile >> s)) {
          cerr << endl << "error when reading letter(s) line " << (i/3)+1 << endl;
          return;
        }
      }

      if (! (inFile >> grades[i])) {
        cerr << endl << "error when reading the " << i+1 << " nth double" << endl;
        return;
      }
      cout << grades[i] << " ";  
    }  

    cout << endl;

    inFile.close(); // CLose input file  
  }  
  else { //Error message   
    cerr << "Can't find input file " << inFileName << endl;  
  }  
}

int main()
{
  readData();
}

编译和执行(文件太短):

pi@raspberrypi:/tmp $ g++ -Wall -Wextra -pedantic v.cc
pi@raspberrypi:/tmp $ cat out.txt 
O      4.972407     6.001956     6.766559
O     -1.537917     5.179561     5.602830
pi@raspberrypi:/tmp $ ./a.out
4.97241 6.00196 6.76656 -1.53792 5.17956 5.60283 
error when reading letter line 3
pi@raspberrypi:/tmp $ 

请注意,在您的操作方式中,您不会返回读取的双精度数,您无法将最终的 0 与初始化设置的数字区分开来。

std::vector<double>使用 a而不是数组也更实用,避免了大小的限制,您可以从向量而不是单独获取大小。


关于问题警告的第一部分,line_no并不表示您阅读的行的排名,因为每次if之一为真时,您都会在没有更新的情况下阅读一行line_no

根据您的附加信息:

我知道我需要的原子,这意味着我知道我需要提取的线,仅此而已。所以我只想问,有什么方法可以改进我的脚本吗?

您的代码等效于那个,更清晰,更小:

if (myfile.is_open())
{
   static const int lineToWrite[] = { 19, 60, 62, 90, 106, -1 };
   const int * p = lineToWrite;
   int line_no = 0;  

   while (line_no <= 108 && getline(myfile,line)) {
     if (++line_no == *p) {
       cout << line << '\n';  
       p += 1;
     }
  }
  myfile.close();
}

但是因为你要写的第二行是

O     -3.375664     6.157090     4.244211  

所以你的代码中的 59 是错误的,我的建议中的第二个数字必须替换为 39(预期的行是第 39 行编号第一行 1)

if (myfile.is_open())
{
   static const int lineToWrite[] = { 19, 39, 62, 90, 106, -1 };
   const int * p = lineToWrite;
   int line_no = 0;  

   while (line_no <= 108 && getline(myfile,line)) {
     if (++line_no == *p) {
       cout << line << '\n';  
       p += 1;
     }
  }
  myfile.close();
}

那写行

P -3.070974 5.319084 5.505385
O 3.375664 6.157090 15.254981

也许62, 90, 106也是错误的,但是您提供的集合不包含第三行的预期行,因此我无法检查/更正


推荐阅读