问题
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