【问题标题】:fread() or fseek() fails on win32 but not mac32fread() 或 fseek() 在 win32 上失败,但在 mac32 上失败
【发布时间】:2013-01-27 19:36:02
【问题描述】:

如果它在 mac32 上运行,它运行良好;如果我在 win32 上运行它,我会在 139 像素处得到一个意外的文件结尾,如果我注释掉 fseek (在我的测试 .tga 图像中它只会寻找 0 字节,所以不应该有区别)我得到在像素 35 处文件意外结束。不过,标题读取完全正常;这只是数据。

对于不同的编译器/架构,这 2 个函数的实现方式是否不同?

struct TGAHeader
{   
    char    idlength;
    char    colourmaptype;
    char    datatypecode;
    short   colourmaporigin;
    short   colourmaplength;
    char    colourmapdepth;
    short   x_origin;
    short   y_origin;
    short   width;
    short   height;
    char    bitsperpixel;
    char    imagedescriptor;
};    

bool read()
{
    printf("Reading TGA file...\n");

    header.idlength = fgetc(TGAFile);
    header.colourmaptype = fgetc(TGAFile);
    header.datatypecode = fgetc(TGAFile);
    fread(&header.colourmaporigin,2,1,TGAFile);
    fread(&header.colourmaplength,2,1,TGAFile);
    header.colourmapdepth = fgetc(TGAFile);
    fread(&header.x_origin,2,1,TGAFile);
    fread(&header.y_origin,2,1,TGAFile);
    fread(&header.width,2,1,TGAFile);
    fread(&header.height,2,1,TGAFile);
    header.bitsperpixel = fgetc(TGAFile);
    header.imagedescriptor = fgetc(TGAFile);

    printf("...TGA file has been read\n\n");
    printf("ID length:         %d\n",header.idlength);
    printf("Colourmap type:    %d\n",header.colourmaptype);
    printf("Image type:        %d\n",header.datatypecode);
    printf("Colour map offset: %d\n",header.colourmaporigin);
    printf("Colour map length: %d\n",header.colourmaplength);
    printf("Colour map depth:  %d\n",header.colourmapdepth);
    printf("X origin:          %d\n",header.x_origin);
    printf("Y origin:          %d\n",header.y_origin);
    printf("Width:             %d\n",header.width);
    printf("Height:            %d\n",header.height);
    printf("Bits per pixel:    %d\n",header.bitsperpixel);
    printf("Descriptor:        %d\n",header.imagedescriptor);

    bytesToRead = header.bitsperpixel / 8;

    if(!errCheck())
    {
        m_bLoaded = false;
        return false;
    }
    //set stream position indicator to the start of the data
    fseek(TGAFile,(header.idlength+(header.colourmaptype * header.colourmaplength)),SEEK_CUR);

    //allocate space to store data
    releaseTGAData();
    pixelData = new unsigned char*[header.width*header.height];
    for(int i=0;i<header.width*header.height;i++)
        pixelData[i] = new unsigned char[bytesToRead];

    for(int i=0;i<header.width * header.height;i++)
    {
        if (fread(pixelData[i],1,bytesToRead,TGAFile) != bytesToRead)
        {
            printf("Unexpected end of file at pixel %d\n",i);
            m_bLoaded = false;
            releaseTGAData();
            return false;
        }
    }
    fclose(TGAFile);

    return true;
}

编辑:这是加载函数

bool load(const char* inFilename = NULL)
{
    m_bLoaded = false;
    if(NULL!=filename)
        delete [] filename;
    filename = new char[128];

    //if the filename has not been selected by the programmer
    if(NULL==inFilename)
    {
        //ask the user for it
        printf("please enter the filename of an uncompressed TGA file:\n");
        scanf("%s", filename);
    }
    else sprintf(filename, inFilename);

    if ((TGAFile = fopen(filename,"r")) == NULL)
    {
        printf("File open failed, please try again\n");
        return false;
    }
    if(!read())
        return false;

    m_bLoaded=true;
    return true;
}

【问题讨论】:

  • 如何打开文件? Windows 和 *nix 上的模式不同。

标签: c++


【解决方案1】:

问题在于您以文本模式打开文件。在 Windows 上,输入函数会将字节序列 0x0d 0x0a(即 Windows 换行序列"\r\n")转换为单个字符。

Windows 很可能还会检查字节 0x19(我认为),它是 CTRL-Z 并且是 Windows 中的文件结尾字符。

【讨论】:

    【解决方案2】:

    您的问题很可能是由于处理非二进制文件时 windows 处理读取文件的方式不同造成的,但更有效的解决方案是使用 #pragma pack 或 attribute (allaligned( 1)) 用于标头结构并在一次 fread 调用中读取整个标头

    【讨论】:

    • 不要在一次fread调用中读取整个标题(或任何struct);这会导致无法维护的代码。例如,更改编译器,尤其是体系结构(x86 与 PowerPC)将以难以检测的方式破坏代码:在一种设置中创建的文件将被很好地读取,但同一文件将被视为已损坏在不同设置中编译时相同的程序。
    • 此外,使用一个fread 几乎没有稍微快一点,因为无论您请求多少字节以及单独将字节移动到@987654325 中都会有轻微延迟,文件都会以大块的形式读入内存IO 操作本身使 @ 与 all at once 相形见绌。
    • 对一个 IO 函数的 20 次调用比单个调用更易于维护。除非您逐字节读写或仅使用字符,否则无论您如何编写代码,这都是您必须解决的问题
    • 还有第二件事,编译器不会优化 I/O api 调用,如果你调用 fread 20 次它会进行 20 次 I/O 操作,如果你不相信我,那么尝试在 for 循环中逐字节写入或读取大文件(1 000 000 字节),并且只需一次调用和时间差。
    • 有关此主题的相关讨论,请参阅:stackoverflow.com/questions/859535/…
    【解决方案3】:

    由于文件包含二进制数据,在 Windows 上调用fopen() 时必须使用"rb" 作为打开模式。在 Unix 上,b 是可选的(但无害),因此如果您的代码是可移植的,请在两个系统上使用相同的模式。

    如果您在 Windows 上打开文件而不指定 b,则会得到“CRLF”映射(因此回车换行序列 "\r\n" 被映射到 newline"\n" — 其中换行只是换行的另一个名称)。并且第一个 control-Z 字节标志着文件的结束。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-19
      • 2019-01-20
      • 1970-01-01
      • 1970-01-01
      • 2018-04-25
      • 1970-01-01
      相关资源
      最近更新 更多