parsing /proc/stat using c++ - c++

I'am new to c++ and a little bit in Linux. I have simple project that need to parse CPU stat from /proc/stat file and compute CPU usage. I have tried doing it on full bash script. but what i need is c++. I just need a little help. /proc/stat gives a lot of numbers and i know different column represent on something. like User,Nice,System,Idle etc. For example i just want to get the Idle value, and store it as Integer using c++, how would i do it? Please Help. What I tried right now is just getting the whole line i need using ifstream and getline()
std::ifstream filestat("/proc/stat");
std::string line;
std::getline(filestat,line);
and what i get is this.
cpu 349585 0 30513 875546 0 935 0 0 0 0
To clarify my question, for example i want to get the 875546 value and store it to an integer using c++. how would i do it? thank you

The format of stat is described in detail under the proc(5) manual page.
You can see it either by running the command man 5 proc from a Linux terminal or online.
The methods described above for parsing the stat file are fine for academic purposes, but a production grade parser should take extra precaution when using these methods.
If you need a production grade parser in C++ for files in /proc, you can check out pfs - A library for parsing the procfs. (Disclaimer: I'm the author of the library)
The biggest issue is usually the comm field (The second field in the file).
According to the man pages, this field is a string that should be "scanned" using some scanf flavor and the formatter %s. But that is wrong!
The comm field is controlled by the application (Can be set using prctl(PR_SET_NAME, ...)) and can easily include spaces or brackets, easily causing 99% of the parsers out there to fail.
And a simple change like that won't just return a bad comm value, it will screw up with all the values that come after it.
The right way to parse the file are one of the following:
Option #1:
Read the entire content of the file
Find the first occurrence of '('
Find the last occurrence of ')'
Assign to comm the string between those indices
Parse the rest of the file after the last occurrence of ')'
Option #2:
Read the PID (the first value in the file)
Read 18 bytes (16 is the largest comm value + 2 for the wrapping brackets)
Extract the comm value from that buffer just like we did for option #1
Find out the actual length of the value, fix your stream and continue reading from there

You really need to study up on how file input works. This should be simple enough. You just need to ignore the first 3 characters "cpu" and then read through 4 integer values:
unsigned n;
if(std::ifstream("/proc/stat").ignore(3) >> n >> n >> n >> n)
{
// use n here...
std::cout << n << '\n';
}
Alternatively if you already have the line (maybe you are reading the file one line at a time) you can use std::istringstream to turn the line into a new input stream:
std::ifstream filestat("/proc/stat");
std::string line;
std::getline(filestat, line);
unsigned n;
if(std::istringstream(line).ignore(3) >> n >> n >> n >> n)
{
// use n here...
std::cout << n << '\n';
}

There are several ways to the problem. You can use regular expression library to get the part of the string or if you know this is always going to the 5th element then you can use this:
std::string text = "cpu 349585 0 30513 875546 0 935 0 0 0 0";
std::istringstream iss(text);
std::vector<std::string> results(std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>());
int data = std::stoi( results[4] ); //check size before accessing
std::cout << data << std::endl;
I hope it helps.

Related

How to keep taking 2 integers as input until program encounters a single integer?

I was given a question where the input will be like:
10 8
4 9
6 12
5 4
3
1
Here I don't know the number of lines that contains 2 integers. Those sets of 2 integers will be taken into an array. But when the program encounters "3", it will start taking input in another array.
I have tried this with
while(cin>>a>>b){ //some porcess with a and b }
but it doesn't work because it recognizes 3 and 1 as another set of two integers. Please help me to solve this problem.
cin >> a >> b skips not only spaces, but any delimeter characters too ('\n', '\t', ' ').
Here you actually may want to read input line-by-line and then check if there are two integers or one. Consider use of std::getline for retrieving each line of text. Then you can use read string as std::istream (like in example in the link above) and read from it with counting, how many numbers you read totally.
So think about your problem. Essentially it is, read one line at a time, and if it contains two numbers do one thing, but if it contains one number do something else.
But the code you have written reads numbers not lines. That is where the problem is.
Instead write your code to read only line at a time, analyse that line to see if it contains one or two numbers (or something else) and then proceed from there.
What you need is the ability to read a line of text into a string, and then read from that string into your numbers. To do that you use an istringstream. Something like this
#include <iostream>
#include <sstream>
#include <string>
int a, b;
string s;
getline(cin, s); // read one line from standard input
istringstream line(s); // put that string to a stream we can read from
if (line >> a) // try and read the first number from the stream
{
// got the first number
if (line >> b) // try and read the second number from the stream
{
// got the second number
...
}
else
{
// only one number
...
}
}
else
{
// didn't get any numbers, some sort of error
...
}

how to ignore n integers from input

I am trying to read the last integer from an input such as-
100 121 13 ... 7 11 81
I'm only interested in the last integer and hence want to ignore all
previous integers.
I thought of using cin.ignore but that won't work here due to
unknown integers (100 is of 3 digits, while 13 is of 2 digits & so on)
I can input integer by integer using a loop and do nothing with them. Is there a better way?
It all depends on the use case that you have.
Reading a none specified number of integers from std::cin is not as easy at it may seem. Because, in contrast to reading from a file, you will not have an EOF condition. If you would read from a file stream, then it would be very simple.
int value{};
while (fileStream >> value)
;
If you are using std::cin you could try pressing CTRL-D or CTRL-Z or whatever works on your terminal to produce an EOF (End Of File) condition. But usually the approach is to use std::getline to read a complete line until the user presses enter, then put this line into a std::istringstream and extract from there.
Insofar, one answer given below is not that good.
So, next solution:
std::string line{};
std::getline(std::cin, line);
std::istringstream iss{line};
int value{};
while (iss >> value)
;
You were asking
Is there a better way?
That also depends a little. If you are just reading some integers, then please go with above approach. If you would have many many values, then you would maybe waste time by unnecessarily converting many substrings to integers and loose time.
Then, it would be better, to first read the complete string, then use rfind to find the last space in the string and use std::stoi to convert the last substring to an integer.
Caveat: In this case you must be sure (or check with more lines of code) that there are no white space at the end and the last substring is really a number. That is a lot of string/character fiddling, which can most probably avoided.
So, I would recommend the getline-stringstream approach.
You can try this simple solution for dynamically ignoring rest of the values except the last given in this problem as shown:
int count = 0;
int values, lastValue; // lastValue used for future use
std::cout << "Enter your input: ";
while (std::cin >> values) {
lastValue = values; // must be used, otherwise values = 0 when loop ends
count++;
}
std::cout << lastValue; // prints
Note: A character must be required to stop the while(), hence it's better put a . at last.
Output example
Enter your input: 3 2 4 5 6 7.
7
Try this:
for( int i=0; i<nums_to_ignore; i++) {
int ignored;
std::cin >> ignored;
}

Seekg not behaving as expected

#include <string>
#include <iostream>
int main() {
std::string str;
char magic[9];
std::cin.read((char *)magic, sizeof(magic));
std::cin.seekg(0, std::ios::beg);
while (std::cin >> str) {
std::cout << str << std::endl;
}
}
my code contains implementation of seekg(0) fucntion on std::cin
it is not behaving as expected on some of the files
when run as
./a.out < filename
those files that it is not behaving as expected have property that they have number of characters(including endline characters and other white spaces) less than 9(9 is the number of characters we read from cin before seekg)
if the file contains more than 9 characters it is behaving as expected
for example:
123456789
will give output as
123456789
while file containing less than 9 characters will not give output
for example:
1234
will give no output
With a file of less than nine characters, you have already attempted to read past the end with your initial read. That means the eof (end of file) and fail flags have been set for the stream and, while seekg may reset eof, it does not reset fail (a).
You can check that by inserting:
cout << "eof/fail=" << cin.eof() << '/' << cin.fail() << '\n';
immediately before and after the seekg. For file sizes of 8, 9, and 10 respectively, you get:
eof/fail=1/1
eof/fail=0/1
eof/fail=0/0
eof/fail=0/0
12345678
eof/fail=0/0
eof/fail=0/0
123456789
You can see the first failure results in no output because the fail bit is still set. The second and third have output because it was never set (the output is the characters shown plus one newline).
To repair this, you can clear the fail bit simply by inserting the following before your seekg:
std::cin.clear();
Then running that code on the eight-character file gives:
eof/fail=1/1
eof/fail=0/0
1234567
showing that the clear has indeed cleared the fail bit.
You might also want to keep in mind that it's not a requirement for a stream to be seekable, especially if it's just coming in via standard input. You may find for certain sized files that you cannot seek back an arbitrary amount if you've read through a large chunk of the stream.
(a) For the language lawyers amongst us, Unformatted input functions (C++11 27.7.2.3/41, C++14 27.7.2.3/41 and C++17 30.7.4.3/41) all have essentially the same text on how seekg works (my emphasis):
After constructing a sentry object, if fail() != true, executes ...

C++ - How to read from a file that has lines that have to be ignored

I have searched anywhere I could to find such thing, believe it or not, for around 3 months...
And it also came to my mind that this is probably something incredibly easy...But after experimenting lots of times, I had no success.
It might be possible to find this somewhere else, but I'm also not sure how to search for it, that's why I'm asking here.
So here's what I want to do:
I have a file like this
[Property]
1 Din 17 M 10 D FFF
2 Dout 12 F 11 F FFF
[Names]
1 Din
2 Dout
The file ends here.
I want to read the whole file into variables, which I already do, for example:
std::ifstream readP("properties.dat");
unsigned short ID;
std::string Name;
unsigned short p2;
char p3;
unsigned short p4;
char gTemp;
std::string fProperty;
while(readP >> ID >> Name >> p2 >> p3 >> p4 >> gTemp >> fProperty);
Now I want to read everything, except the [Property] and the [Names], because obviously with these fields in the text, the reader will not read, because they aren't being read from the while loop.
What would be a good way to accomplish this?
You must do a bit more elaborate parsing.
So, try this:
Read a line.
Decide how to process it.
A) Skip.
B) Parse into variables.
You cannot "ignore" lines, but you could "skip" them.
I would suggest reading sequentially line by line with std::getline then skip the uninteresting stuff.
Notice that lines don't really exist in most file systems (at least Posix ones). Lines are just conventionally terminated by \n but a file is a sequence of bytes.
You should read line by line, and the determin whether you are going to process this line.
You can check if the first character is [ or not.
http://www.cplusplus.com/reference/string/string/at/
if (str.at(0) != '[')
{
//Process this line by splitting the string with space ect
}

Read external file and assign variables in xml-like input

In a file which has an xml like format (but is not xml)
<mgrwt event="1">
<rscale> 1 1234</rscale>
<asrwt>0 4234</asrwt>
<pdfrwt beam="1"> 1 2 0.11790045E+00 0.22210436E+03</pdfrwt>
<pdfrwt beam="2"> 1 -2 0.92962177E-02 0.22210436E+03</pdfrwt>
<totfact> 0.34727485E+01</totfact>
<matchscale> 0.10000000E+11 0.41999999E+02 0.61496031E+02</matchscale>
</mgrwt>
I would like with C++ to read in the file (this I know ;-) block-by block , and then assign variables to sub-components of each block, - I know for example that all numbers in
<pdfrwt beam="1"> 1 2 0.11790045E+00 0.22210436E+03</pdfrwt>
ie 1 2 0.11790045E+00 0.22210436E+03 are always numbers, and never string,so question is , how can I strip/read from each line in each block the separated-by-space numbers ?
I also tried this Read input numbers separated by spaces but could not help me..
thanks
I suggest reading line by line and using std::istringstream to parse the string.
For example, you could find out where the start of the number is by searching for the first >.
Either advance the stringstream by this amount or ...
Create a substring contain all the text before the '<'.
Create an istringstream using this substring.
Then try something like:
unsigned int first, second;
double third, fourth;
string_stream >> first >> second >> third >> fourth;