所以我只需要一些关于如何修复这个程序的指导,所以我需要阅读一个名为“infile.txt”的文件,文件里面是描述应该绘制的形状的说明(一个大写字符, 即 R,T,D,S,E) 然后它给出应该填充形状的字符然后是 int 中的列数和行数。 txt 文件如下所示:
T & 4
S @ 6
T x 5
R * 5 7
D $ 7
D + 5
R = 4 3
E
现在,我什至不太确定是否可以使用 switch 语句完成此操作,因为 infile 被读取为字符串。但是后来我对如何从 switch 语句进行更改感到困惑。无论如何,在读取 infile 后,我必须输出绘制到 outfile 的形状。希望这是有道理的,我是 super 入门级,几乎不知道自己在做什么。所以这是我的代码:
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
void draw_rect (char out_char, int rows, int columns); // Draws a rectangle shape
void draw_square (char out_char, int rows); //Draws a square shape
void draw_triangle (char out_char, int rows);// Draws a triangle shape
void draw_diamond (char out_char, int rows); // Draws a diamond shape
//void dimension_instructions(char value);
int main()
{
ofstream outfile;
ifstream infile;
int row, col;
bool exit = false;
char value;
char code;
infile.open("infile.txt");
outfile.open("outfile.txt");
if(!infile.good())
{
cout << "failed to open\n";
}else
{
string buffer;
while(!infile.eof())
{
getline(infile, buffer);
cout << buffer << endl;
}
while(!exit)
{
cout << "Enter your shape R for rectangle, T for triangle, D for diamond, S for square, and E to exit" << endl;
cin >> code;
switch(code)
{
case 'R':
dimension_instructions(code);
cin >> value >> row >> col;
draw_rect(value, row, col);
break;
case 'T':
dimension_instructions(code);
cin >> value >> row;
draw_triangle(value, row);
break;
case 'D':
dimension_instructions(code);
cin >> value >> row;
draw_diamond(value, row);
break;
case 'S':
dimension_instructions(code);
cin >> value >> row;
draw_square(value, row);
break;
case 'E':
cout << "Exiting";
exit = true;
break;
default:
cout << "Invalid input, try again" << endl;
}
}
infile.close();
}
outfile.close();
return 0;
}
/*void dimension_instructions(char value)
{
if (value == 'R')
{
cout << "Enter your character rows and columns values." << endl;
}else
{
cout << "Enter your character and row values" << endl;
}
}*/
void draw_diamond (char out_char, int rows)
{
int space = 1;
space = rows - 1;
for (int i = 1; i <= rows; i++)
{
for (int k = 1; k <= space; k++)
{
cout << " ";
}
space--;
for( int k = 1; k <= 2*i-1; k++)
{
cout << out_char;
}
cout << endl;
}
space = 1;
for (int i = 1; i <= rows; i++)
{
for(int k = 1; k <= space; k++)
{
cout << " ";
}
space++;
for(int k = 1; k <= 2*(rows-i)-1; k++)
{
cout << out_char;
}
cout << endl;
}
}
void draw_triangle (char out_char, int rows)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j <= i; j++)
{
cout << out_char;
}
cout << endl;
}
}
void draw_square (char out_char, int rows)
{
for (int i = 0; i < rows; i++)
{
for (int i = 0; i < rows; i++)
{
cout << out_char;
}
cout << endl;
}
}
void draw_rect (char out_char, int rows, int columns)
{
for (int i = 0; i < rows; i++)
{
for (int i = 0; i < columns; i++)
{
cout << out_char;
}
cout << endl;
}
}
最佳答案
好的,根据您的评论,我知道您卡在哪里以及为什么。 (如果您还没有完成,您还需要在 draw_square
和 draw_rect
中修复循环变量)。
您的主要问题是不了解如何处理每行不同数量的输入。当你遇到这个问题时,你已经正确地选择了 getline
来将每一行读入 buffer
,但是接下来呢?这就是 stringstream
与众不同的地方。
为什么?有两个原因,(1)它允许您使用基本 iostream buffer
逐字解析 >>
的内容,以及(2)它允许您在需要时循环直到读取尽可能多(或尽可能少)的流结束) 存在的 token ,当您到达行尾时停止(这是不可能的 >>
在文件流本身上使用,因为 >>
会消耗空格,并且会愉快地跳过每个 '\n'
)
有了这个,你的代码真的只需要一点重构(修复困惑逻辑的一个花哨的词)。
开始不要硬编码文件名或在代码中使用魔数(Magic Number)。使用 main()
的参数将文件名传递给您的程序并在需要时声明常量。还要避免使用不会消耗前导空格的 char
。 cin >> a_char;
阅读 ' '
(空格)和阅读其他内容一样快乐。
还要适本地确定变量的范围。您不需要声明所有变量,以便它们在整个 main()
中可见。在适当的范围内声明/定义它们。
例如:
...
#include <sstream>
...
int main (int argc, char **argv) { /* don't hardcode filenames */
ifstream infile; /* infile and buffer are the only variables */
string buffer; /* that need to be scoped at main() */
将文件名作为参数传递时,只需验证您的用户提供了文件名或在保释前向他提供使用信息。
if (argc < 2) { /* validate at least 1 argument is provided */
cerr << "error: insufficient input.\n"
"usage: " << argv[0] << " filename.\n";
return 1;
}
你有你的论点,现在验证你打开你的文件进行阅读:
infile.open (argv[1]); /* open filename provided as 1st argument */
if(!infile.good()) { /* validate file is open for reading */
cerr << "failed to open infile\n";
return 1;
}
现在对控制读取循环的方式进行了重要更改。
getline
提供您所需要的一切。简单地循环,而 getline
为 buffer
提供良好的输入,例如 while (getline(infile, buffer)) { /* loop reading each line */
int row, col; /* remaining variables scoped inside */
string value, code; /* your read loop, use strings */
stringstream ss(buffer); /* create stringstream from buffer */
现在您正在阅读每一行,并且您已经从
stringstream
创建了一个 buffer
来解析您的字符——除了 注意 如何将 value, code
声明为 string
而不是 char
——这提供了一种简单的方法来跳过仅读取非白色的前导空白人物。然后你可以简单地访问你需要的字符,例如value[0]
。验证你对
code
有很好的阅读 if (!(ss >> code)) { /* validate code read into string */
cerr << "error: ss >> code.\n";
break;
}
然后,只需重复对所需数据的读取进行相同的验证,并在每个
switch() case:
中调用正确的函数,例如 switch (code[0]) /* switch on 1st char of code */
{
case 'R':
if ((ss >> value >> row >> col)) /* validate read */
draw_rect (value[0], row, col); /* draw rect */
else /* or handle error */
cerr << "error: 'R' invalid format '" << buffer << "'\n'";
break;
case 'T':
if ((ss >> value >> row)) /* ditto for rest of shapes */
draw_triangle(value[0], row);
else
cerr << "error: 'T' invalid format '" << buffer << "'\n'";
break;
case 'D':
if ((ss >> value >> row))
draw_diamond(value[0], row);
else
cerr << "error: 'D' invalid format '" << buffer << "'\n'";
break;
case 'S':
if ((ss >> value >> row))
draw_square(value[0], row);
else
cerr << "error: 'S' invalid format '" << buffer << "'\n'";
break;
case 'E':
cout << "Exiting\n";
goto exitE; /* goto to break nested loops / scopes */
break;
default:
cout << "Invalid input, try again" << endl;
}
}
exitE:; /* the lowly goto provides a simple exit */
除了关闭
infile
(这将自动发生,但手动显示您对关闭的考虑并没有什么坏处)之外,仅此而已。注意 但是使用
goto
而不是 exit
的标志。虽然 goto
没有得到太多关注,但它仍然有一个非常宝贵的目的——干净地打破嵌套循环和范围的能力。不要用它来跳出函数(longjmp
是技术限制),但它可以大大简化你打破嵌套循环和向下跳几行的逻辑。 (在相同的设置中,跳过通常在错误条件下循环结束时执行的代码也很有用)所以了解它的用途。您可以自由使用标志,但您可能会在许多设置中找到
goto
清洁器。有了它,你可以把它完全(暂时忽略
outfile
)与类似的东西放在一起:#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
using namespace std;
void draw_rect (char out_char, int rows, int columns); // Draws a rectangle shape
void draw_square (char out_char, int rows); //Draws a square shape
void draw_triangle (char out_char, int rows);// Draws a triangle shape
void draw_diamond (char out_char, int rows); // Draws a diamond shape
int main (int argc, char **argv) { /* don't hardcode filenames */
ifstream infile; /* infile and buffer are the only variables */
string buffer; /* that need to be scoped at main() */
if (argc < 2) { /* validate at least 1 argument is provided */
cerr << "error: insufficient input.\n"
"usage: " << argv[0] << " filename.\n";
return 1;
}
infile.open (argv[1]); /* open filename provided as 1st argument */
if(!infile.good()) { /* validate file is open for reading */
cerr << "failed to open infile\n";
return 1;
}
while (getline(infile, buffer)) { /* loop reading each line */
int row, col; /* remaining variables scoped inside */
string value, code; /* your read loop, use strings */
stringstream ss(buffer); /* create stringstream from buffer */
if (!(ss >> code)) { /* validate code read into string */
cerr << "error: ss >> code.\n";
break;
}
switch (code[0]) /* switch on 1st char of code */
{
case 'R':
if ((ss >> value >> row >> col)) /* validate read */
draw_rect (value[0], row, col); /* draw rect */
else /* or handle error */
cerr << "error: 'R' invalid format '" << buffer << "'\n'";
break;
case 'T':
if ((ss >> value >> row)) /* ditto for rest of shapes */
draw_triangle(value[0], row);
else
cerr << "error: 'T' invalid format '" << buffer << "'\n'";
break;
case 'D':
if ((ss >> value >> row))
draw_diamond(value[0], row);
else
cerr << "error: 'D' invalid format '" << buffer << "'\n'";
break;
case 'S':
if ((ss >> value >> row))
draw_square(value[0], row);
else
cerr << "error: 'S' invalid format '" << buffer << "'\n'";
break;
case 'E':
cout << "Exiting\n";
goto exitE; /* goto to break nested loops / scopes */
break;
default:
cout << "Invalid input, try again" << endl;
}
}
exitE:; /* the lowly goto provides a simple exit */
infile.close();
return 0;
}
void draw_diamond (char out_char, int rows)
{
int space = 1;
space = rows - 1;
for (int i = 1; i <= rows; i++)
{
for (int k = 1; k <= space; k++)
{
cout << " ";
}
space--;
for( int k = 1; k <= 2*i-1; k++)
{
cout << out_char;
}
cout << endl;
}
space = 1;
for (int i = 1; i <= rows; i++)
{
for(int k = 1; k <= space; k++)
{
cout << " ";
}
space++;
for(int k = 1; k <= 2*(rows-i)-1; k++)
{
cout << out_char;
}
cout << endl;
}
}
void draw_triangle (char out_char, int rows)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j <= i; j++)
{
cout << out_char;
}
cout << endl;
}
}
void draw_square (char out_char, int rows)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < rows; j++)
{
cout << out_char;
}
cout << endl;
}
}
void draw_rect (char out_char, int rows, int columns)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
cout << out_char;
}
cout << endl;
}
}
( 注意: 循环变量
i, j
修复了 draw_square
和 draw_rect
这似乎是复制/粘贴错误——除此之外,没有对您的形状函数进行任何更改)示例使用/输出
$ ./bin/drawshapes dat/drawshapes.txt
&
&&
&&&
&&&&
@@@@@@
@@@@@@
@@@@@@
@@@@@@
@@@@@@
@@@@@@
x
xx
xxx
xxxx
xxxxx
*******
*******
*******
*******
*******
$
$$$
$$$$$
$$$$$$$
$$$$$$$$$
$$$$$$$$$$$
$$$$$$$$$$$$$
$$$$$$$$$$$
$$$$$$$$$
$$$$$$$
$$$$$
$$$
$
+
+++
+++++
+++++++
+++++++++
+++++++
+++++
+++
+
===
===
===
===
Exiting
如果您还有其他问题,请仔细查看并告诉我。
关于c++ - 如何读取 infile 中的指令,然后使用 infile 中的参数将函数形状写入 outfile,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55153945/