2017-04-18 45 views
0

我试图写在磁盘上的PPM文件。 PPM是一个简单的图像格式,由ASCII图像头和像素的字节数组的:的std :: ofstream的是加入回车(CR; r)后 n自动

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的在报头中的每\n之前添加\r

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 wile无回车?

换句话说:如何正确初始化ofstream使它不写\r

+0

ios :: binary是正确的,但它已经打开。你没有二进制模式再次打开它?如果我只是说if(out.is_open())out <<“hello \ n”;再想一想,不,我不能重现你的问题。即使我打电话打开第二次,但没有指定模式,这让我感到惊讶。 –

+0

我得到一个空文件,我可以重现。也许它是特定于Windows的? –

+0

我没有去调用一个ostream运算符的极端。我会尽力重现整个事情。 –

回答

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

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

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

就像你已经想通了,使用std :: IOS ::二进制是解决方案。 std :: ofstream构造函数应该打开这个文件,所以删除对out.open()的调用。

+0

我发现当我使用该标志时,我得到一个空文件作为输出。我可能使用它错了。 –

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/)所以不要这样做。

+1

“*宇宙的其余部分本身仅使用'\ n' *” - 实际上并非每个人都这么做。有些平台使用'\ r'而不是'\ n'。所以,你有三种可能的处理方式:1)裸CR,2)裸LF,和3)CR + LF。 –

+0

我在你的文章中使用了确切的代码,它会产生空文件。毕竟,这也是我的问题。 –

+0

我引用了你的代码来引用它。

相关问题