std::ofstream is adding carriage return (CR; \r) after \n automatically

我怕爱的太早我们不能终老 提交于 2019-12-12 03:59:12

问题


I am trying to write PPM file on disk. PPM is a simple image format that consists of ASCII image header and byte array of pixels:

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

This is my PPM class (simplified):

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

This is how I write the image to 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;
}

This is how I use this API:

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

The image seems ok, except for the fact that ofstream adds \r before every \n in the header:

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

This is unacceptable. I tried to change the initialization code like this:

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

But that just creates empty file. Can someone explain how to correctly write PPM wile without carriage returns?

In other words: How to correctly initialize the ofstream so that it writes without \r?


回答1:


By incorrectly opening the file a second time, you place the stream in a fail state. Just calling clear() makes it work, but this is not ideal.

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

The correct way to open your file is to pass all the arguments together, filename and mode, wherever you chose to put it. Either in the constructor, or with open, but not both. So just use your original code plus the correct mode for Windows.

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



回答2:


Like you have figured out, using std::ios::binary is the solution. The std::ofstream constructor should open the file, so remove the call to out.open().




回答3:


What you're seeing is an artifact of Windows. Windows uses \r\n ( aka Carriage Return/Line Feed, or 0x0D 0x0A) pairs to mark end-of-line. The rest of the universe uses just \n by itself. This leads to the crazy Windows text mode for files, which translates all occurrences of \r\n in a file to a single \n when being read. On write, it translates all occurrences of \n to a \r\n pair. Opening your file in std::ios::binary mode prevents this translation.

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

You don't have to call open it twice. Why did you think you do? That will mess up your ofstream object because [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/) So don't do that.



来源:https://stackoverflow.com/questions/43481143/stdofstream-is-adding-carriage-return-cr-r-after-n-automatically

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!