将无符号 char * 保存到文件并读取它会在 Windows 中产生意外结果

Saving unsigned char * to file and reading it produces unexpected results in Windows

提问人:Brian 提问时间:9/8/2023 最后编辑:Brian 更新时间:9/13/2023 访问量:111

问:

下面的代码在 Ubuntu 中工作正常。我正在使用 glad 和 glfw 为 openGL 加载纹理。

#include <vector>
#include <iostream>
#include <fstream>
using namespace std;


#include <glad/glad.h>
#include <GLFW/glfw3.h>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"


const bool loadResavedImages = true;
GLFWwindow* window;

void loadTexture( char const *filename, unsigned int &texture )
{
    
    cout << filename << endl;
    
    //static int texturesLoaded = 0;
    glGenTextures( 1, &texture );
    glBindTexture( GL_TEXTURE_2D, texture );
    
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    
    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load( true );
    
    unsigned char * data = NULL;
    
    char newFileName[ 50 ];
    snprintf( newFileName, sizeof(char) * ( strlen( filename ) - 3 ), "%s", filename );
    
    
    ifstream readFile( newFileName );
    
    if( loadResavedImages && readFile )
    {
        if( readFile.is_open() )
        {
            readFile >> width >> height;
            data = new unsigned char[ width * height * 4 ];
            readFile.read( (char *)data, width * height * 4 );
        }
        else
        {
            cout << "shouldnt happendfklgdskf" << endl;
        }
        
        readFile.close();
    }
    else
    {
        readFile.close();
        
        data = stbi_load( filename, &width, &height, &nrChannels, 0 );
        //saved images
        
        //save to a file
        
        ofstream saveFile( newFileName );
        
        if( saveFile.is_open() )
        {
            saveFile << width << " " << height;
            saveFile.write( (char *)&data[ 0 ], width * height * 4 );
        }
        else
            cout << "WHY!?!?!SDSGGFGAGSSG" << endl;
        
        
        saveFile.close();
    }   
    
    for( int i = 0; i < width * height * 4; i++ )
    {
        cout << int( data[ i ] ) << " ";
    }
    
    if ( data )
    {
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
            
        glGenerateMipmap( GL_TEXTURE_2D );
    }
    else
    {
        cout << "failed to load texture " << filename <<endl;
        exit( 0 );
    }
    
    if( loadResavedImages && readFile )
        delete [] data;
    else
    {
        stbi_image_free( data );
    }
    
}




// main
int main()
{
    glfwInit();
        
    window = glfwCreateWindow( 500, 500, "Window", NULL, NULL );
    
    glfwMakeContextCurrent( window );
    
    if ( !gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress ) )
    {
        cout << "Failed to initialize GLAD" << endl;
        return 0;
    }
    
    //load texture
    unsigned int texture;
    loadTexture( "twirl.png", texture );
    
    return 0;
}

当在 Ubuntu 上加载这个 400x400 纹理“旋转.png”时,它主要是空的透明空间,我们预计最后会得到一堆 0。

如果您从数据文件“twirl”保存并重新加载此纹理,则预计它会在 Ubuntu 上输出相同的内容。

如果在 Windows 上运行,则最后的输出会有所不同。我们的输出是

205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205 205

...等 而不是预期的空白空间

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

等。。

为什么这会发生在 Windows 上以及如何让它正确加载和保存?

C++ 输入 输出

评论

4赞 user4581301 9/8/2023
建议:在十六进制编辑器中打开文件,看看它到底是什么进入了文件。鉴于单行片段,如果您只是给他们汽车的方向盘,我们不能比汽车修理工更好地为您提供帮助。缺少太多上下文。
4赞 Remy Lebeau 9/8/2023
没有“unsigned char * to char * conversion”,它只是一个指针类型转换,实际数据是没有改变的。此外,可以简化为 。在任何情况下,请提供一个最小的可重复示例,说明您是如何分配和填充的,以及在保存/加载文件之前。将 s 写入文件,然后以 s 的形式读回它们意味着未定义的行为在代码中的某个地方起作用。十进制是十六进制,一系列字节是正在使用的未初始化内存的常见指标,具体取决于编译器配置(char *)&data[ 0 ](char *)datadatawidthheight02052050xCD0xCD
2赞 Brian 9/8/2023
退休忍者回答了这个问题。执行二进制模式后正确加载的内容
3赞 user4581301 9/8/2023
Brian,给最小可重现示例(MRE)链接一个很好的通读。它描述了制作最佳代码示例的过程。它也是几种非常有效的调试技术的提炼。如果您通过在 MRE 中隔离问题来开始提问过程,您通常会发现,在开始在 Stack Overflow 中键入任何内容之前,您就已经完成了问题。提出好的问题是艰苦的工作,通常回报是不必问问题。
2赞 drescherjm 9/8/2023
ifstream readFile( newFileName );也许该文件甚至没有打开,所以它根本没有读取任何内容。您显示的输出将与该输出一致,这意味着如果您的代码无法读取,我希望数据与您在调试模式下运行 msvc 时收到的数据完全相同。原因是@RemyLebeau很久以前提到的。MSVC 在调试模式下用0xcd填充堆内存:stackoverflow.com/questions/127386/...msvc

答:

1赞 Brian 9/10/2023 #1

由于在默认文本模式下打开文件时会自动进行字符转换,因此更改了两行代码

ifstream readFile( newFileName );
//...
//...
ofstream saveFile( newFileName );
//...

ifstream readFile( newFileName, ios_base::binary );
//...
//...
ofstream saveFile( newFileName, ios_base::binary ); 
//...

修复了 Windows 中的问题。