C++: проблемы с манипулированием файлом PPM

Я работаю над программой, которая может выполнять различные эффекты и манипуляции с файлом PPM. Однако по причинам тестирования он использует cin, а не входной файл. Предполагается, что он способен выполнять несколько эффектов одновременно, но у меня возникают проблемы, даже если я получу один правильный. Я запускаю removeBlue() в строке, которая будет работать, затем попробую еще раз с другими значениями, и вместо этого будет удален красный или зеленый. Что-то в этом роде. Там много кода, поэтому я постараюсь включить только то, что необходимо.

#include <vector>
#include <stdlib.h>
#include <cstdlib>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std;

class SimpleImageEffect
{
public:
    virtual void processImage(vector<Point> &points) = 0;
};

class RemoveRed : public SimpleImageEffect
{
public:

    virtual void processImage(vector<Point> &points)
    {
        for (Point& p : points)
        {
            p.setRed(0);
        }
    }
};
//Just an example of one of the effect classes. 
//The code in them is correct, so I won't include the others unless needed.

vector<Point> parse_line(string line)
{
    istringstream scanner{line};
    vector<Point> result{};
    int red = -1;
    int green = -1;
    int blue = -1;
    int counter = 0;

    while(scanner.good())
    {
        if (counter == 0)
        {
            counter++;
            scanner >> red;
        }
        else if (counter == 1)
        {
            counter++;
            scanner >> green;
        }
        else if (counter == 2)
        {
            scanner >> blue;
            Point p{ red, green, blue };
            result.push_back(p);
            counter = 0;
        }
    }
    return result;
}

void readFromCin()
{
    string line = "";

    vector<string> lines_in_file{};
    int i, effect_choice;
    SimpleImageEffect *effect = nullptr;

    getline(cin, line);

    while (line.length() > 0)
    {
        lines_in_file.push_back(line);
        getline(cin, line);
    }

    for (int i = 0; i < lines_in_file.size(); i++)
    {
        if (lines_in_file[i] != "P3")
        {
            effect_choice = strToInt(lines_in_file[i]);
        }

        else if (lines_in_file[i] == "P3")
        {
            cout << lines_in_file[i] << endl;
            cout << lines_in_file[i+1] << endl;
            cout << lines_in_file[i+2] << endl;
        }

        vector<Point> points = parse_line(lines_in_file[i]);

        if (effect_choice == 1) effect = new RemoveRed;
        if (effect_choice == 2) effect = new RemoveGreen;
        if (effect_choice == 3) effect = new RemoveBlue;
        if (effect_choice == 4) effect = new NegateRed;
        if (effect_choice == 5) effect = new NegateGreen;
        if (effect_choice == 6) effect = new NegateBlue;
        if (effect_choice == 7) effect = new AddNoise;
        if (effect_choice == 8) effect = new HighContrast;
        if (effect_choice == 9) effect = new ConvertToGrayscale;

        effect->processImage(points);

        for (auto p : points)
        {
            cout << p;
            cout << endl;
        }
    }
}

int main(int argc, char** argv)
{
    string menu_choice;
    getline(cin, menu_choice);
    if (menu_choice == "1")
    {
        readFromFile();
    }
    else
    {
        readFromCin();
    }
    return 0;
}

Так, например, запустив его с вводом

2
1
P3
1 1
255
50 50 50

вернусь

P3
1 1
255
0 50 50

но если я запускаю его с

2
3
P3
1 2
255
50 50 50
1 2 3

это возвращается

P3
1 2
255
0 50 50
0 2 3

Я понятия не имею, что является причиной проблемы, поэтому любая помощь будет принята с благодарностью. Благодарю.

1 ответ

Логическая структура вашего алгоритма сильно пахнет, вот что я вижу:

  • читать все не пустые строки в lines_in_file (выглядит хорошо для меня)
  • для КАЖДОЙ строки (проблематично, требуется дополнительная логика во внутреннем цикле):
    • если не "P3", попробуйте разобрать строку [EVERY] как целое и установить effect_choice (из вашего кода непонятно, что происходит в строках с несколькими целыми числами, но, судя по описанию вашей проблемы, первое целое число успешно анализируется strToInt функция)
    • если "P3", текущая строка и следующие две копируются на выход
    • [КАЖДАЯ] строка анализируется как вектор триплетов чисел
    • effect устанавливается новый эффект для фактического значения effect_choice (для КАЖДОЙ линии delete effect в конце вы теряете память при подсчете количества строк. Также ваши текущие эффекты выглядят так, как будто они могут быть реализованы как статические функции типа функции "процесс", так что вам не нужно выделять каждый из них, просто сохраните конкретный адрес памяти запрашиваемой функции. И ты называешь это processImageпока вы обрабатываете только строку, а не все изображение.
    • effect запускается для текущих линейных триплетов
    • линейные триплеты выводятся
  • цикл до следующей строки (!)

Так, например, для ввода:

2
3
P3
1 2
255
50 50 50
1 2 3

Я считаю (не могу запустить его, так как вы не предоставили много кода) это происходит:

строки читаются, и для каждой конкретной строки это происходит:

строка "2": effect_choice = 2, effect = RemoveGreenноль триплетов разбирается на points, RemoveGreen::processImage() пробегать пустой вектор, печатать пустой вектор (то есть ничего).

строка "3": effect_choice = 3, effect = RemoveBlueноль триплетов разбирается на points, RemoveBlue::processImage() пробег по пустому вектору, пустой вектор напечатан.

строка "P3": линии: {"P3", "1 2", "255"} печатаются, ноль триплетов анализируются в points, RemoveGreen::processImage() пробег по пустому вектору, пустой вектор напечатан.

строка "1 2": effect_choice = 1, effect = RemoveRedноль триплетов разбирается на points, RemoveRed::processImage() пробег по пустому вектору, пустой вектор напечатан.

строка "255": effect_choice = 255ноль триплетов разбирается на points, RemoveRed::processImage() пробег по пустому вектору, пустой вектор напечатан.

строка "50 50 50": effect_choice = 50одна тройка {50, 50, 50} разобрали в points, RemoveRed::processImage() пробеги по нему, модифицированные триплетные выходы {0, 50, 50},

строка "1 2 3": effect_choice = 1, effect = RemoveRedодна тройка {1, 2, 3} разобрали в points, RemoveRed::processImage() пробеги по нему, модифицированные триплетные выходы {0, 2, 3},

Все это должно быть четко видно в отладчике, при этом перешагивая через код, так что вы, вероятно, не отлаживаете его, что вызывает у меня отрицательный ответ на вопрос, и со временем вам придется заплатить огромную боль, поскольку отладка без отладчика намного сложнее,

Кроме того, написание кода без размышлений об алгоритме и архитектуре кода повышает вероятность отладки, поэтому вы потратили здесь еще больше времени, начав с написания кода.

Вы должны сначала спроектировать некоторый алгоритм и архитектуру кода (какие данные обрабатываются, как, когда требуется новая память, как она будет освобождена, где код должен зацикливаться, где его нужно пропускать или выполнять только один раз, и т. Д.).

Напишите только обзор того, как он будет работать, в однострочные комментарии, затем разбейте слишком общие комментарии на более простые шаги, пока они не будут реализованы несколькими строками кода C++, и перемещайте / изменяйте их, пока не почувствуете, что требуемый алгоритм будет реализован с помощью добавлен минимальный "бесполезный" (большинство комментариев делает то, что действительно запрашивается, например, "установить красный в точке на ноль"), а любая обработка / подготовка / перемещение / и т. д. сводится к минимуму только в тех случаях, когда вы не можете избежать этого более умным дизайн). (например, в вашем текущем коде вы можете читать заголовок файла без зацикливания, и начинать зацикливание только после того, как данные пикселей будут добавлены)

Затем напишите код, начните, вероятно, с какого-то пустого определения функции, чтобы вы уже могли "запустить" его в отладчике и проверить, работает ли пустота, затем внедрите комментарий (или небольшую группу из них), который, по вашему мнению, достаточно ясен для реализации и может легко тестироваться (нет большой зависимости от еще не реализованных деталей). Отладка + тестирование нового кода. Если это работает, попробуйте очистить исходный код, чтобы удалить все, что действительно не нужно, имена переменных в процессе работы и т. Д.... Затем убедитесь, что это работает в окончательной версии.

И сделайте это снова для другого комментария (группы), пока реализация не будет завершена.

Использование модульного тестирования делает процесс записи короткого кода, test + debug, clean-source еще проще, особенно в таких случаях, когда ввод-вывод - чистые данные, поэтому легко вводить специализированные входные данные теста в тест и убедитесь, что были получены ожидаемые выходные данные.

Другие вопросы по тегам