c++ - 如何读取 infile 中的指令,然后使用 infile 中的参数将函数形状写入 outfile

标签 c++ file-io shapes

所以我只需要一些关于如何修复这个程序的指导,所以我需要阅读一个名为“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_squaredraw_rect 中修复循环变量)。

您的主要问题是不了解如何处理每行不同数量的输入。当你遇到这个问题时,你已经正确地选择了 getline 来将每一行读入 buffer ,但是接下来呢?这就是 stringstream 与众不同的地方。

为什么?有两个原因,(1)它允许您使用基本 iostream buffer 逐字解析 >> 的内容,以及(2)它允许您在需要时循环直到读取尽可能多(或尽可能少)的流结束) 存在的 token ,当您到达行尾时停止(这是不可能的 >> 在文件流本身上使用,因为 >> 会消耗空格,并且会愉快地跳过每个 '\n' )

有了这个,你的代码真的只需要一点重构(修复困惑逻辑的一个花哨的词)。

开始不要硬编码文件名或在代码中使用魔数(Magic Number)。使用 main() 的参数将文件名传递给您的程序并在需要时声明常量。还要避免使用不会消耗前导空格的 charcin >> 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 提供您所需要的一切。简单地循环,而 getlinebuffer 提供良好的输入,例如
    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_squaredraw_rect 这似乎是复制/粘贴错误——除此之外,没有对您的形状函数进行任何更改)

示例使用/输出
$ ./bin/drawshapes dat/drawshapes.txt
&
&&
&&&
&&&&
@@@@@@
@@@@@@
@@@@@@
@@@@@@
@@@@@@
@@@@@@
x
xx
xxx
xxxx
xxxxx
*******
*******
*******
*******
*******
      $
     $$$
    $$$$$
   $$$$$$$
  $$$$$$$$$
 $$$$$$$$$$$
$$$$$$$$$$$$$
 $$$$$$$$$$$
  $$$$$$$$$
   $$$$$$$
    $$$$$
     $$$
      $

    +
   +++
  +++++
 +++++++
+++++++++
 +++++++
  +++++
   +++
    +

===
===
===
===
Exiting

如果您还有其他问题,请仔细查看并告诉我。

关于c++ - 如何读取 infile 中的指令,然后使用 infile 中的参数将函数形状写入 outfile,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55153945/

相关文章:

c++ - std::invoke 和 std::function 有什么区别?

c++ - 参数化构造函数不是拷贝

windows - 具有部分对象(CreateFileMapping)的 I/O 是否比基本 api(Read/WriteFile)更快?

javascript - 如何有效地将网络流缓冲到 JavaScript 中的文件中?

c++ - 如何修改文件夹中所有文件的代码

java - 如何在 JavaFX 2.0 中创建几何形状?

c++ - 关于C++内联函数的两个问题

c++ - 使用链表 C++ 添加 2 个多项式表达式

r - ggplot shape = Unicode(例如 "\u2198"或 LaTeX\searrow 之类的箭头)?

css - 对 Angular 楔形 CSS - 在浏览器中边到边居中