我需要从.txt文件中提取复杂值,并将其分配给4个数组(4个复杂矩阵3 * 3),然后在它们之间执行+,-,*,/,*(-0.8 + 1.6i)操作。我需要使用什么来在txt文件中查找并获取这些复杂值?
这是.txt文件的简短版本。
# file.txt
# some text bla bla 123
# same
-2.3-14.6i,9.1
7.1+2.8i-7.1-11.7i
# bla bla
现在我做了什么。
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;
}
首先,C ++库提供了一个std::complex,您可以使用它来代替comp
结构。使用标准的复杂类型将提供比struct
更多的功能。但是,使用struct comp
进行学习以解析文件中所需的信息并没有错。
无论您使用提供的std::complex
还是自己的struct comp
,如果要存储所有复杂值以便它们可用,您将需要创建一个std::vector来保存所有值,而不是简单的旧数组comp
。 (让STL处理内存管理和存储-与从头开始自行编写相比,它不容易出错)
请勿在代码中使用Magic-Numbers或Hardcode Filenames。 (这就是main()
接受参数int main (int argc, char **argv)
的原因。将文件名作为参数传递给main()
或将文件名作为输入。(如果未提供默认文件名,则可以提供默认文件名)。为文件名指定一个参数,并声明您的std::vector<comp>
并打开并验证您的文件是否打开,可以执行以下操作:
...
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 */
}
...
要从文件中读取复数值,您需要一次将一行数据读入arr
。然后,您想从std::stringstream
创建一个arr
,使您可以使用','
分隔符从字符串流中读取包含复杂值的字符串。 (您不能将','
分隔符用于从文件中读取,getline
不会知道行的结尾,而忽略每行末尾的'\n'
来查找下一个','
,就可以跳过该行) )
因此,您只需将行放入stringstream中并从stringstream中读取(stringstream中只有一行数据,因此没有机会读取末尾的数据)。例如:
...
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 */
...
就像您正在通过上面的循环从文件中循环读取getline
的行一样,现在您只需以类似的方式使用第二个循环从字符串流中读取由','
分隔的字符串。在循环中,您将需要获取当前字符串的长度,保留从字符串开头的偏移量变量,以与substr
一起使用,以告诉stod
从哪里开始读取下一个值,您将想要声明一个pos
(位置)值,以std::stod
填充,告诉您在将行的当前部分转换为double
时使用了多少个字符,因此可以将其添加到偏移量中以了解开始阅读下一个。参见std::stof, std::stod, std::stold。
您还将想要声明一个temporary struct comp
实例,以填充转换为double
的值,如果对两个vector
值的解析都成功,则可以将其添加到x, y
中:
...
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 */
...
使用std::stod
转换时,必须使用try {...} catch (exception) {...}
捕捉转换期间的任何错误,以便您可以正确处理该错误。读取实部(x
)后,将使用转换中使用的字符数(由s
报告)更新pos
中的偏移量,以了解从何处开始虚部的转换(如果没有虚部,则将值设置为零),例如
...
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:由于您已经阐明了虚数之间将存在','
,因此您可以在调用中简单地使用s
而不是s.substr(off)
将实数部分转换为两倍。如果substr
是可选的以允许使用','
的定界符,然后检查下一个字符是否为'i'
,这将需要在第一次调用时使用','
,则需要使用off == 1
>
剩下的就是将您填充的临时struct comp
添加到向量中,例如
... cmplx.push_back(tmp); /* add temporary comp to vector */ } }
基本上就是这样。您已填满
std::vector<comp> cmplx
,将从文件中读取所有复数值。您可以简单地输出它们以验证结果,例如
... std::cout << "results\n\n"; /* output results */ for (auto& c : cmplx) std::cout << "(" << c.x << ", " << c.y << "i)\n"; }
将其完全放入:
#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"; }
示例使用/输出
将数据保存在文件dat/cmplx.txt
中,运行程序并分析文件中的值将导致:
$ ./bin/parse_complex dat/cmplx.txt results (-2.3, -14.6i) (9.1, 0i) (7.1, 2.8i) (-7.1, -11.7i)
仔细检查,如果还有其他问题,请告诉我。