Reading a particular line from an output file

Pages: 12
Hi,

Well I have made a few changes to your code. One thing is that the seekg() does not
work on std::cin. So I have made it so it takes another parameter at the command line
for the input file name. Now it reads directly from the file rather than through std::cin.

Also I have added a few helper functions. The << and >> input output operators for
particle and a function to read a line of data as a particle called getparticle(std::istream&, particle&).

The other thing is your figure for average line length. I came up with about 28-30 from
the data you posted before. Your figure of 84 seems very large are you sure that's correct?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <stdio.h>
using namespace std;

struct particle
{
	double x;
	double y;
	double z;
	double radius;
	double dencity;
	int type;
};

// Define input and output operators to make code easier:
std::istream& operator>>(std::istream& is, particle& p)
{
	is >> p.x;
	is >> p.y;
	is >> p.z;
	is >> p.radius;
	is >> p.dencity;
	is >> p.type;
	return is;
}

// Now you can write: in >> p;

std::ostream& operator<<(std::ostream& os, const particle& p)
{
	os << p.x << '\t';
	os << p.y << '\t';
	os << p.z << '\t';
	os << p.radius << '\t';
	os << p.dencity << '\t';
	os << p.type << '\n';
	return os;
}

// Now you can write: out << p;

// Helpful function for reading in numerics
template<typename T>
bool read_numeric(std::istream& is, T& t)
{
	char ch;
	return is >> t and (!is.get(ch) or std::isspace(ch));
}

// Helpful function to read in a line as a particle
std::istream& getparticle(std::istream& is, particle& p)
{
	std::string line;
	if(std::getline(is, line))
	{
		std::istringstream iss(line);
		iss >> p;
	}
	return is;
}

int main(int argc, char* argv[])
{
	// make input filename a parameter
	if(argc < 3)
	{
		std::cerr << "Usage: particle_filter <y-value> <filename>" << std::endl;
		return 1;
	}

	// get input parameter y
	double y;
	std::istringstream iss(argv[1]);
	if(!read_numeric(iss, y))
	{
		std::cerr << "Error Argument y was not valid, "
			<< argv[1] << std::endl;
		return 1;
	}

	// open input file (can't use std::cin with seekg()
	std::ifstream ifs(argv[2]);
	if(!ifs)
	{
		std::cerr << "Error: opening file " << argv[2] << std::endl;
		return 1;
	}

	bool next = false;
	int count = 0;
	particle p;
	while(getparticle(ifs, p))
	{
		if (p.y != y && count == 0) // For jumping once only
		{
			int line = int (2024807438/15000); //no. of particles / layers
			int size = 84;	// average size of each line
			int jump = int (y/0.0021334 + 0.5);  // layers to be skipped

//			My test data
//			line = 10; // debug
//			size = 28; // debug
//			jump = 2; // debug

			int pos = line * size * jump;	// total jump
			std::cout << pos << std::endl;
			ifs.seekg(pos);  // was seeking on wrong stream
			count++;

			// MUST align read pointer after seekg()
			std::string align;
			std::getline(ifs, align); // moves to start of next line

			 // read in new particle in here
			// to satisfy rest of loop
			getparticle(ifs, p);
		}

		if(p.y == y)
		{
			cout << p;
			next = true;
		}

		if (p.y != y && next == true)
		{
			return 1;
		}
	}

	return 0;
}
Hey Galik,

Thanks a ton!! This really helped
I had one last query. Now, instead of specifying the value of Y to be extracted in the command line, I am specifying it in the code itself (As I need to access this code using another code). Now, instead of extracting particles corresponding to a single value of Y (say y = 0), i want to extract all the particles corresponding to a set of Y values (These values of Y will be in sequence - i.e. y = 0, y = 1, y = 2 and so on till say y = 10). I tried using a for loop, but I was getting stuck. Could you please help me out. Thanks again!! :)
Can you post the code with your for loop? There is no reason why that approach should not work.
Hi,
I have just added a for loop where the code actually reads in the particles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

  y = 0.00482236; // sepcify the value of y from which I need to start reading
  for (int i = 0; i < layers; i++) // layers = no. of Y layers that I want to read
  {
    while (getparticle(ifs, p))
    {
      if (p.dencity != 1.95 && p.y > 0.000001) // there are other kind of particles as well. I want to skip those, hence this condition
      {
        if (p.y != y && count == 0)
        {
	  int line = int (TotalNumOfParticles/TotalCastingLayers);
	  int size = 42;
	  int jump = int ((y - rad)/cast_step + 0.5);

	  int pos = line * size * jump;
	  cout << pos << endl;
	  ifs.seekg(pos);
	  count++;

	  std::string align;
	  std::getline(ifs, align);

	  getparticle(ifs, p);
        }
        if(p.y == y)
        { 
	  ofstream fout ("layer.cast" , ios::app | ios::out); // Am reading the data out to this file
	  fout << p;
	  fout.close();
	  next = true;
        }
        if (p.y > y && next == true)
        {
	  cout << "true" << endl;
	  break;
        }
      }
    }
    y += cast_step; // Increment the value of y to the value = the Y value of the next set of particles. cast_step is the increment in the value of y between different sets of particles
  }

The rest of the code is the same. (I have used - "using namespace std; " to avoid the std:: )
Now the problem is that it reads out the particles corresponding to the first value of y (i.e. y = 0.00482236 in this case). However, it stops reading out from then onwards. Could you please help me out...
I think perhaps you need to re-initialise your check variable with each value of y
by moving them inside the loop:

1
2
3
4
5
6
7
8
  y = 0.00482236; // sepcify the value of y from which I need to start reading
  for (int i = 0; i < layers; i++) // layers = no. of Y layers that I want to read
  {
	bool next = false;
	int count = 0;
        while (getparticle(ifs, p))
        {
            // ... 
Nopes...that didnt help either.
I think I know what the problem is -
We start off with y = 0.00482236. I have specified the y as being of the type 'double'. We have also calculated the value of cast_step - which is the increment in the value of y and which is also of the type double.

So after the no. of particles in the layer y = 0.00482236 have been counted, the value of y in the data file changes to y = 0.00695576 (which is the next value of y). In the code, we do (y + cast_step) - I think that the new value of y that is generated here in the code must differ slightly (say it becomes 0.006955763 or something instead of 0.00695576) since we are adding two 'doubles'.

Could this possibly be the problem. Can you think of any way how I can overcome this??
First of all I have spotted another problem. Your if() at lines 7 & 8 is in the wrong place. It prevents your jump code from working. I think you need to put that if() further down, after the jump code:

Also the loop skips wrong if one layer follows another. I have rearranged the code slightly. I put the reading loop into a separate function and the for() loop now calls that function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <stdio.h>
using namespace std;

struct particle
{
	double x;
	double y;
	double z;
	double radius;
	double dencity;
	int type;
};

// Define input and output operators to make code easier:
std::istream& operator>>(std::istream& is, particle& p)
{
	is >> p.x;
	is >> p.y;
	is >> p.z;
	is >> p.radius;
	is >> p.dencity;
	is >> p.type;
	return is;
}

// Now you can write: in >> p;

std::ostream& operator<<(std::ostream& os, const particle& p)
{
	os << p.x << '\t';
	os << p.y << '\t';
	os << p.z << '\t';
	os << p.radius << '\t';
	os << p.dencity << '\t';
	os << p.type << '\n';
	return os;
}

// Now you can write: out << p;

// Helpful function to read in a line as a particle
std::istream& getparticle(std::istream& is, particle& p)
{
	std::string line;
	if(std::getline(is, line))
	{
		std::istringstream iss(line);
		iss >> p;
	}
	return is;
}

int process(std::ifstream& ifs, std::ostream& os, double y)
{
	// reset pos is required for when first element
	// of current y value was read from previous call
	// This forces a recalculation of file position
	ifs.seekg(0);
	bool next = false;
	int count = 0;
	particle p;
	while(getparticle(ifs, p))
	{
		if (p.y != y && count == 0) // For jumping once only
		{
			int line = int (2024807438/15000); //no. of particles / layers
			int size = 84;	// average size of each line
			int jump = int (y/0.0021334 + 0.5);  // layers to be skipped

//			My test data
//			line = 10; // debug
//			size = 28; // debug
//			jump = int(y/0.001 + 0.5); // debug

			// ensure we don't overshoot
			int pos = (line * size * jump) - (2 * size);
			if(pos < 0) { pos = 0; }
			// total jump
			ifs.seekg(pos);  // was seeking on wrong stream
			count++;

			if(pos != 0)
			{
				// MUST align read pointer after seekg()
				std::string align;
				std::getline(ifs, align); // moves to start of next line
			}

			// read in new particle in here
			// to satisfy rest of loop
			getparticle(ifs, p);
		}

		if(p.y == y)
		{
			// additional conditions here
			if(p.dencity != 1.95 && p.y > 0.000001)
			{
				os << p;
			}
			next = true;
		}

		if(p.y != y && next == true)
		{
			return 0;
		}
	}

	return 0;
}
int main(int argc, char* argv[])
{
	// make input filename a parameter
	if(argc < 2)
	{
		std::cerr << "Usage: particle_filter <filename>" << std::endl;
		return 1;
	}

	// open input file
	std::ifstream ifs(argv[1]);
	if(!ifs)
	{
		std::cerr << "Error: opening file " << argv[2] << std::endl;
		return 1;
	}

	// Might as well just open this once
	ofstream fout("layer.cast", ios::app | ios::out);

	double y = 0.00482236; // initial y value
	double cast_step = 0.001; // step value
	int layers = 2; // number of layers to extract
//	y = 0.002; // debug
	for(size_t i(0); i < layers; ++i, y+= cast_step)
	{
		process(ifs, fout, y);
	}

	return 0;
}


This works on my sample test data. Let me know if you have any problems.
Topic archived. No new replies allowed.
Pages: 12