I need to extract complex values from the .txt file and assign it to 4 arrays(4 complex matrix 3*3) and after do +, -, *, /, * ( -0.8+1.6i ) operations between them. What do I need to use to find and get these complex values in the txt file?
it's a short version of the .txt file.
# file.txt
# some text bla bla 123
# same
-2.3-14.6i,9.1
7.1+2.8i-7.1-11.7i
# bla bla
Now what I did.
struct comp
{
double x, y;
};
comp arr[100];
ifstream infile("C:\\Users\\Nick Leeker\\Desktop\\data.txt");
if (!infile) // Does file open correctly?
{
cout << "error -1\n\n";
return -1;
}
else
{
cout << "file is open\n\n";
return 1;
}
First, the C++ library provides a std::complex that you can use instead of using your comp
struct. Using the standard complex type will provide a great deal more functionality than your struct
. However, there is nothing wrong with using your struct comp
for learning purposes in parsing the information you need from your file.
Whether you use the std::complex
provided or your own struct comp
, if you want to store all complex values so they are available you will want to create a std::vector to hold all the values rather than a plain old array of comp
. (let the STL handle the memory management and storage -- it will be much less error prone than writing it on your own from scratch)
Don't use Magic-Numbers or Hardcode Filenames in your code. (that is why main()
takes the arguments int main (int argc, char **argv)
. Pass your filename as an argument to main()
or take the filenames as input. (it's fine to provide a default filename if one isn't given) To validate you have an argument given for the filename and declaring your std::vector<comp>
and opening and validating you file is open cand be done as follows:
...
struct comp { /* your struct of complex type */
double x, y;
};
int main (int argc, char **argv) {
if (argc < 2) { /* validate 1 argument given for filename */
std::cerr << "error: filename required as argument"
"usage: " << argv[0] << " filename\n";
return 0;
}
std::vector<comp> cmplx; /* vector of comp */
std::string arr; /* string to read each line */
std::ifstream infile (argv[1]); /* open file given as 1st argument */
if (!infile.good()) { /* validate file open */
std::cerr << "error: file open failed '" << argv[1] << "'.\n";
return 1; /* don't return negative values to the shell */
}
...
To read the complex values from your file, you will want to read a line of data at a time into arr
. You then want to create a std::stringstream
from arr
that will allow you to read from the stringstream separating the strings containing complex values by using a ','
delimiter. (you can't use the ','
delimiter for the read from the file, getline
would not know where the line ends and skip right by ignoring the '\n'
at the end of each line looking for the next ','
)
So you simply put the line in the stringstream and read from the stringstream (there is only one line worth of data in the stringstream, so there is no chance of reading beyond the end). For example:
...
while (getline (infile, arr)) { /* read each line */
if (arr.at(0) == '#') /* if comment line, get next */
continue;
std::string s; /* string to read from ss */
std::stringstream ss(arr); /* create stringstream from arr */
...
Just as you are looping reading lines with getline
from the file with the loop above, you now just use a second loop in like manner to read the strings separated by ','
from the stringstream. Within the loop you will want to get the length of the current string, keep a variable of the offset from the beginning of the string for use with substr
to tell stod
where to start reading for your next value, and you will want to declare a pos
(position) value to be filled by std::stod
telling you how many characters were used in the conversion of current part of the line to a double
so you can add that to your offset to know where to start reading the next. See std::stof, std::stod, std::stold.
You will also want to declare a temporary struct comp
instance to fill with the values converted to double
that you can then add to your vector
if the parse to both x, y
values are successful:
...
while (getline (ss, s, ',')) { /* read with ',' delimiter */
size_t len = s.length(), /* length to check if done reading */
off = 0, /* offset from beginning of s */
pos = 0; /* chars reported used by stod */
comp tmp; /* temporary struct to fill */
...
When converting with std::stod
you must use a try {...} catch (exception) {...}
to catch any error during conversion so you can handle the error properly. After you read the real-part (x
), you will update the offset in s
with the number of characters used in the conversion (reported by pos
) to know where to start your conversion for the imaginary part (if there is no imaginary part, just set the value to zero), e.g.
...
try { /* you must validate conversion with exceptions */
tmp.x = stod (s.substr(off), &pos);
}
catch (const std::exception & e) { /* error in 1st conversion */
std::cerr << "error invalid tmp.x: "<< e.what() << '\n';
continue; /* get next potential complex no. */
}
off += pos; /* real part obtained, update offset with pos */
if (len > off) { /* check chars remain to parse imaginary */
try { /* validate with exception */
tmp.y = stod (s.substr(off), &pos);
}
catch (const std::exception & e) { /* warn on failed parse */
std::cerr << "no value for tmp.y: "<< e.what() << '\n';
}
}
else /* if no chars for imaginary part, set to zero */
tmp.y = 0;
...
(note: since you clarified that there will be a ','
between the imaginary numbers, you can simply use s
instead of s.substr(off)
in the call to convert the real-part to double. The use of substr
was needed if the ','
was optional to allow use of the delimiter of 'i'
and then check if the next character was ','
which would require the use of off == 1
on the first call)
All that remains is adding the temporary struct comp
you filled to your vector, e.g.
...
cmplx.push_back(tmp); /* add temporary comp to vector */
}
}
That is basically it. You have your std::vector<comp> cmplx
filled will all the complex values read from the file. You can simply output them to verify the results, e.g.
...
std::cout << "results\n\n"; /* output results */
for (auto& c : cmplx)
std::cout << "(" << c.x << ", " << c.y << "i)\n";
}
Putting it altogether you would have:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
struct comp { /* your struct of complex type */
double x, y;
};
int main (int argc, char **argv) {
if (argc < 2) { /* validate 1 argument given for filename */
std::cerr << "error: filename required as argument"
"usage: " << argv[0] << " filename\n";
return 0;
}
std::vector<comp> cmplx; /* vector of comp */
std::string arr; /* string to read each line */
std::ifstream infile (argv[1]); /* open file given as 1st argument */
if (!infile.good()) { /* validate file open */
std::cerr << "error: file open failed '" << argv[1] << "'.\n";
return 1; /* don't return negative values to the shell */
}
while (getline (infile, arr)) { /* read each line */
if (arr.at(0) == '#') /* if comment line, get next */
continue;
std::string s; /* string to read from ss */
std::stringstream ss(arr); /* create stringstream from arr */
while (getline (ss, s, ',')) { /* read with ',' delimiter */
size_t len = s.length(), /* length to check if done reading */
off = 0, /* offset from beginning of s */
pos = 0; /* chars reported used by stod */
comp tmp; /* temporary struct to fill */
try { /* you must validate conversion with exceptions */
tmp.x = stod (s.substr(off), &pos);
}
catch (const std::exception & e) { /* error in 1st conversion */
std::cerr << "error invalid tmp.x: "<< e.what() << '\n';
continue; /* get next potential complex no. */
}
off += pos; /* real part obtained, update offset with pos */
if (len > off) { /* check chars remain to parse imaginary */
try { /* validate with exception */
tmp.y = stod (s.substr(off), &pos);
}
catch (const std::exception & e) { /* warn on failed parse */
std::cerr << "no value for tmp.y: "<< e.what() << '\n';
}
}
else /* if no chars for imaginary part, set to zero */
tmp.y = 0;
cmplx.push_back(tmp); /* add temporary comp to vector */
}
}
std::cout << "results\n\n"; /* output results */
for (auto& c : cmplx)
std::cout << "(" << c.x << ", " << c.y << "i)\n";
}
Example Use/Output
With your data in the file dat/cmplx.txt
, running the program and parsing values from the file would result in:
$ ./bin/parse_complex dat/cmplx.txt
results
(-2.3, -14.6i)
(9.1, 0i)
(7.1, 2.8i)
(-7.1, -11.7i)
Look things over and let me know if you have further questions.
Edit - Actual Input File Differs In Format
After looking at the input file on past.openSUSE.org, the only change I saw that needed adjustments was the change from checking the start of the comment from the first column to anywhere in the line. I've updated the test to: if (arr.find("#") != std::string::npos)
The complete code would be:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
struct comp { /* your struct of complex type */
double x, y;
};
int main (int argc, char **argv) {
if (argc < 2) { /* validate 1 argument given for filename */
std::cerr << "error: filename required as argument"
"usage: " << argv[0] << " filename\n";
return 0;
}
std::vector<comp> cmplx; /* vector of comp */
std::string arr; /* string to read each line */
std::ifstream infile (argv[1]); /* open file given as 1st argument */
if (!infile.good()) { /* validate file open */
std::cerr << "error: file open failed '" << argv[1] << "'.\n";
return 1; /* don't return negative values to the shell */
}
while (getline (infile, arr)) { /* read each line */
if (arr.find("#") != std::string::npos) /* if comment line, get next */
continue;
std::string s; /* string to read from ss */
std::stringstream ss(arr); /* create stringstream from arr */
while (getline (ss, s, ',')) { /* read with ',' delimiter */
size_t len = s.length(), /* length to check if done reading */
off = 0, /* offset from beginning of s */
pos = 0; /* chars reported used by stod */
comp tmp; /* temporary struct to fill */
try { /* you must validate conversion with exceptions */
tmp.x = stod (s.substr(off), &pos);
}
catch (const std::exception & e) { /* error in 1st conversion */
std::cerr << "error invalid tmp.x: "<< e.what() << '\n';
continue; /* get next potential complex no. */
}
off += pos; /* real part obtained, update offset with pos */
if (len > off) { /* check chars remain to parse imaginary */
try { /* validate with exception */
tmp.y = stod (s.substr(off), &pos);
}
catch (const std::exception & e) { /* warn on failed parse */
std::cerr << "no value for tmp.y: "<< e.what() << '\n';
}
}
else /* if no chars for imaginary part, set to zero */
tmp.y = 0;
cmplx.push_back(tmp); /* add temporary comp to vector */
}
}
std::cout << "results\n\n"; /* output results */
for (auto& c : cmplx)
std::cout << "(" << c.x << ", " << c.y << "i)\n";
}
Example Use/Output
Copying and pasting the file from paste.opensuse.org into the file dat/complexfile.txt
, I get the following output:
$ ./bin/parse_complex2 dat/complexfile.txt
results
(-2.3, -14.6i)
(9.1, -2.5i)
(7.1, 2.8i)
(14.5, 12.2i)
(2.6, -13.2i)
(11.7, 0.7i)
(14, 1.6i)
(4.1, -10i)
(-7.3, 3.9i)
(9.9, 4.9i)
(5.6, -1.2i)
(15, 7.4i)
(-11.7, -4.2i)
(-8.2, 5.8i)
(9, -12i)
(13.8, 3.7i)
(-10.1, -8.9i)
(13.9, 6.4i)
(-3.2, -11.2i)
(-10.8, 13.9i)
(0.3, 1i)
(-1.1, -13.9i)
(-0.2, 0.1i)
(-11.8, 0.6i)
(-7, -6.9i)
(-14, 2.5i)
(-12.6, 10.7i)
(-0.3, 0.5i)
If you are still having problems, we will need to compare contents of the file at the byte level. A hexdump of the input file as copied is hexdump -Cv file
Example Building With VS, Running On Windows
Just to satisfy myself, I went to a windows box, opened the VS Developers Command Prompt (latest version of VS17) and built the program, e.g.
C:\Users\david\Documents\dev\src-cpp>cl /W3 /wd4996 /Ox /EHsc /Foobj /Febin/parse_complex2 /Tp parse_complex2.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27034 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
parse_complex2.cpp
Microsoft (R) Incremental Linker Version 14.16.27034.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:bin/parse_complex2.exe
obj.obj
I then went to matrix and clicked Download
and saved the file under the default filename in the dat
directory below where I built the executable, e.g.
C:\Users\david\Documents\dev\src-cpp>dir dat
Volume in drive C is Windows
Volume Serial Number is ECC4-9A8B
Directory of C:\Users\david\Documents\dev\src-cpp\dat
11/12/2019 05:06 PM <DIR> .
11/12/2019 05:06 PM <DIR> ..
11/12/2019 05:06 PM 4,916 40665451.txt
1 File(s) 4,916 bytes
2 Dir(s) 1,787,090,620,416 bytes free
Then I ran the program (I always put executables in the bin
directory below the current):
C:\Users\david\Documents\dev\src-cpp>bin\parse_complex2.exe dat\40665451.txt
results
(-2.3, -14.6i)
(9.1, -2.5i)
(7.1, 2.8i)
(14.5, 12.2i)
(2.6, -13.2i)
(11.7, 0.7i)
(14, 1.6i)
(4.1, -10i)
(-7.3, 3.9i)
(9.9, 4.9i)
(5.6, -1.2i)
(15, 7.4i)
(-11.7, -4.2i)
(-8.2, 5.8i)
(9, -12i)
(13.8, 3.7i)
(-10.1, -8.9i)
(13.9, 6.4i)
(-3.2, -11.2i)
(-10.8, 13.9i)
(0.3, 1i)
(-1.1, -13.9i)
(-0.2, 0.1i)
(-11.8, 0.6i)
(-7, -6.9i)
(-14, 2.5i)
(-12.6, 10.7i)
(-0.3, 0.5i)
All fine no problems. So I am really perplexed as to why you are having issues reading the file and getting the correct results. Try the above, step-by-step and let me know if you are still having problems. We will get it solved....