std :: ofstream自动在\ n之后添加回车符(CR; \ r \ n)

时间:2017-04-18 19:59:54

标签: c++ iostream ofstream

我正在尝试在磁盘上写PPM文件。 PPM是一种简单的图像格式,由ASCII图像头和像素字节数组组成:

P6\n
width height\n
255\n
[width*height*3 bytes total]

这是我的PPM课程(简化):

class PPMImage
{
protected:
    friend std::istream& operator >>(std::istream &inputStream, PPMImage &other);
    friend std::ostream& operator <<(std::ostream&, const PPMImage&);
    size_t width;
    size_t height;
    // eg. "P6"
    std::string magicNumber;
    // Normally 255
    uint16_t maxBrightness;
    std::vector<std::vector<ImagePixel>> pixels;
};

这就是我将图像写入std::ofstream的方式:

std::ostream& operator <<(std::ostream &output, const PPMImage &other) {
    // Writing header - THIS IS WHERE THE PROBLEM IS!
    output<<"P6\n"<<other.width<<'\n'<<other.height<<'\n'<<other.maxBrightness<<'\n';
    // The rest is pretty much irrelevant
    size_t position = output.tellp();
    output.seekp(position+other.width*other.height*3);
    // Force the stream to be specific size
    const char zero = 200;
    output.write(&zero, 1);
    // Write the image
    output.seekp(position);
    for(size_t y=0, yl=other.height; y<yl; ++y) {
        for(size_t x=0, xl=other.width; x<xl; ++x) {
            output.write((char*)&(other.pixels[y][x].r), 1);
            output.write((char*)&(other.pixels[y][x].g), 1);
            output.write((char*)&(other.pixels[y][x].b), 1);
        }
    }
    return output;
}

这就是我使用此API的方式:

std::ofstream out;
out.open("copy.ppm");
if(!out.is_open()) {
    // error and exit here
}
out<<image;
out.close();

图片似乎没问题,除了ofstream在标头中的每个\r之前添加\n这一事实:

P6\r\n
width height\r\n
255\r\n
[width*height*3 bytes total]

这是不可接受的。我试着像这样更改初始化代码:

std::ofstream out("copy.ppm", std::ios::binary);
// I wonder why I have to mention "copy.ppm" twice...
out.open("copy.ppm");

但这只是创建空文件。有人可以解释如何在没有回车的情况下正确编写PPM诡计吗?

换句话说:如何正确初始化ofstream,使其在没有\r的情况下进行写入?

3 个答案:

答案 0 :(得分:1)

就像你已经想到的那样,使用std :: ios :: binary是解决方案。 std :: ofstream构造函数应该打开文件,因此删除对out.open()的调用。

答案 1 :(得分:1)

通过第二次错误地打开文件,您将流置于失败状态。只需调用clear()就可以了,但这并不理想。

#include <fstream>
#include <iostream>

class CustomObject{
public:
    std::string message;
    explicit CustomObject(const std::string &text) : message(text) {}
    friend std::ostream& operator <<(std::ostream&, const CustomObject&);
};

std::ostream& operator <<(std::ostream &output, const CustomObject &other) {
    if (output.fail()){
        std::cout << "the stream is in a fail state due to the bad open" << std::endl;
        output.clear();
    }

    output << "P6\n" << other.message.c_str() << '\n';
    return output;
}

int main()
{
    std::string filename("something.ppm");
    std::ofstream out(filename, std::ios::binary);
    out.open(filename);
    out << CustomObject("Hello");
}

打开文件的正确方法是将所有参数,文件名和模式一起传递给您,无论您选择放置它。在构造函数中,或者使用open,但不是两者。因此,只需使用原始代码和Windows的正确模式。

std::ofstream out;
out.open("copy.ppm", std::ios::binary);
if(!out.is_open()) {
    // error and exit here
}
out<<image;
out.close();

答案 2 :(得分:0)

您所看到的是Windows的工件。 Windows使用\r\n(也称为回车符/换行符或0x0D 0x0A)对来标记行尾。宇宙的其余部分本身仅使用\n。这导致文件的疯狂Windows文本模式,在读取时将文件中所有出现的\r\n转换为单个\n。在写入时,它会将所有出现的\n转换为\r\n对。以std::ios::binary模式打开文件会阻止此转换。

std::ofstream out("copy.ppm", std::ios::binary);
// I wonder why I have to mention "copy.ppm" twice...
out.open("copy.ppm");

你不必打电话打开两次。你为什么认为你呢?这会弄乱你的ofstream对象,因为[i]f the stream is already associated with a file (i.e., it is already open), calling this function fails.http://www.cplusplus.com/reference/fstream/ofstream/open/)所以不要这样做。