Как мне прочитать инструкции в файле infile, а затем записать формы функции в файл, используя параметры из файла - PullRequest
1 голос
/ 14 марта 2019

Так что мне просто нужно некоторое руководство относительно того, как я мог бы исправить эту программу, поэтому мне нужно прочитать файл с именем "infile.txt", а внутри файла находятся инструкции, описывающие форму, которую следует нарисовать (Aодиночный символ в верхнем регистре, а именно 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 читается как строка.Но потом я запутался в том, как вообще изменить выражение оператора.В любом случае, после того, как инфиль прочитан, я должен вывести форму, нарисованную в аутфиле.Надеюсь, это имеет смысл, я супер начальный уровень и почти не знаю, что я делаю.Итак, вот мой код:

#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;
    }
}

1 Ответ

1 голос
/ 14 марта 2019

Хорошо, на основании вашего комментария я вижу, где вы застряли и почему. (также вам нужно исправить переменные цикла в draw_square и draw_rect, если вы еще этого не сделали).

Ваша основная проблема - не понимание того, как обрабатывать различное количество входов на строку. Когда вы столкнулись с этой проблемой, вы правильно выбрали getline, чтобы прочитать каждую строку в buffer, но что тогда? Вот где stringstream имеет значение.

Почему? Две причины: (1) это позволяет вам анализировать содержимое buffer слово за словом с базовым iostream >> и (2) оно, когда это необходимо, позволяет вам зацикливаться до конца чтения потока, как много (или так мало) токенов, которые присутствуют, останавливаются, когда вы достигаете конца строки (что невозможно при использовании >> в самом файловом потоке, так как >> занимает пустое пространство и с радостью пропускает право на каждый '\n')

Учитывая это, ваш код действительно нуждался в небольшом рефакторинге (причудливое слово для исправления перемешанной логики).

Для начала Не кодировать жестко имена файлов или использовать магические числа в своем коде. Используйте аргументы для 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 для анализа ваших символов - кроме note как 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

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...