【问题标题】:C++: Issues with manipulating a PPM fileC++:处理 PPM 文件的问题
【发布时间】:2017-03-03 10:32:05
【问题描述】:

我正在开发一个可以对 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

我完全不知道是什么导致了这个问题,所以任何帮助都将不胜感激。谢谢。

【问题讨论】:

    标签: c++ ppm


    【解决方案1】:

    你的算法逻辑结构很臭,我看到的是这样的:

    • 将所有非空行读入lines_in_file(我觉得不错)
    • 对于每一行(有问题,需要在内部循环中添加额外的逻辑):
      • 如果不是“P3”,请尝试将 [EVERY] 行解析为整数并设置 effect_choice(从您的代码中不清楚,在提供多个整数的行上会发生什么,但判断根据您的问题描述,strToInt 函数成功解析了第一个整数)
      • 如果为“P3”,则将当前行和下两行复制到输出
      • [EVERY] 行被解析为三元组数字的向量
      • effecteffect_choice 的实际值的新效果设置(对于每一行,你也没有delete 最后effect,所以你在每行计数中泄漏内存。还有你当前的效果看起来它们可能被实现为“进程”函数类型的静态函数,所以你不需要分配它们中的每一个,只需存储请求函数的特定内存地址即可。你称之为processImage,而你正在处理只有一行,而不是整个图像。
      • effect 为当前行三元组运行
      • 行三连音输出
    • 循环到下一行 (!)

    例如输入:

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

    我相信(无法运行它,因为您没有提供大量代码)会发生这种情况:

    读取行,并且每个特定行都会发生这种情况:

    第“2”行:effect_choice = 2effect = RemoveGreen、零三元组解析为pointsRemoveGreen::processImage() 运行空向量,打印空向量(即什么都没有)。

    第“3”行:effect_choice = 3effect = RemoveBlue、零三元组解析为 pointsRemoveBlue::processImage() 运行空向量,打印空向量。

    “P3”行:打印行:{"P3", "1 2", "255"},零三元组解析为 pointsRemoveGreen::processImage() 运行空向量,打印空向量。

    “1 2”行:effect_choice = 1effect = RemoveRed、零三元组解析为 pointsRemoveRed::processImage() 运行空向量,打印空向量。

    “255”行:effect_choice = 255,零三元组解析为 pointsRemoveRed::processImage() 运行空向量,打印空向量。

    “50 50 50”行:effect_choice = 50,一个三元组 {50, 50, 50} 解析为 pointsRemoveRed::processImage() 运行,修改后的三元组输出 {0, 50, 50}

    “1 2 3”行:effect_choice = 1effect = RemoveRed、一个三元组 {1, 2, 3} 解析为 pointsRemoveRed::processImage() 运行,修改后的三元组输出 {0, 2, 3}

    所有这些都应该在调试器中清晰可见,同时跨过代码,所以你可能没有调试它,这让我对这个问题投了反对票,随着时间的推移,你会付出巨大的痛苦,因为没有调试器的调试是困难得多。

    另外,在不考虑算法和代码架构的情况下编写代码,更可能需要调试,因此您从编写代码开始在这里浪费了更多时间。

    您应该首先设计一些算法和代码架构(处理什么数据、如何处理、何时需要新内存、如何释放它、代码需要循环的位置、需要跳过的位置或只运行一次,等等)。

    只写出它如何在单行 cmets 中工作的概述,然后将过于通用的 cmets 拆分为更简单的步骤,直到它们可以通过几行 C++ 代码实现,然后移动/修改它们,直到你觉得需要的算法会以最少的“杂乱无章”来实现(大多数 cmets 确实如此,真正要求的是什么,例如“将红色点设置为零”,并且任何处理/准备/移动/等仅在您无法避免的情况下被最小化它通过更聪明的设计)。 (例如在您当前的代码中,您可以在不循环的情况下读取文件的标题,并且只有在像素数据涌入后才开始循环)

    然后编写代码,可能从一些空函数定义开始,这样您就可以在调试器中“运行”它并验证空性是否有效,然后实现您认为足够清晰的注释(或其中的一小部分)实现并且可以很容易地测试(对尚未实现的部分没有很大的依赖)。调试 + 测试新代码。如果可行,请尝试清理源以删除任何不需要的内容、正在进行的变量名称等...然后验证它在最终版本中是否有效。

    对另一个评论(组)再做一次,直到实现完成。

    使用单元测试可以更轻松地编写短代码、测试+调试、清理源代码,尤其是在这种情况下,I/O 是纯数据,因此很容易提供专门的测试输入数据进入测试,并验证产生了预期的输出数据。

    【讨论】:

      猜你喜欢
      • 2015-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多