坑的描述
用fwrite
把数据写入文件,再用fread
读取,发现后半部分的数据可能是错的。
原因:原本要写入文件的数据中,有0x0A,如果用的是文本模式打开的文件流,在windows下0x0A会被转换为0x0D和0x0A
其实windows下的git bash每次git add后都有类似的提示,只是一直没太注意:
先说结论
用fread
或fwrite
的时候,如果是要写入字符,那么打开的文件、读取的文件,用字符模式(w
和r
)
FILE* fin = fopen("filename", "w");
fread(buf, sizeof(char)*num_elem, 1, fin);
fclose(fin);
FILE *fout = fopen("filename", "r");
fwrite(buf, sizeof(char)*num_elem, 1, fout);
fclose(fout);
如果是要写入非字符的数据,例如float数组、int数组等,则一定要用二进制模式打开文件(wb
和rb
)(尽管在linux和mac下你的结果也许一直没问题,但是保不准到了windows下会出错):
FILE* fin = fopen("filename", "wb");
fread(buf, sizeof(float)*num_elem, 1, fin);
fclose(fin);
FILE *fout = fopen("filename", "rb");
fwrite(buf, sizeof(float)*num_elem, 1, fout);
fclose(fout);
原因:字符模式打开的文件,在windows下,遇到0x0A进行写入(也就是\n
)会替换为0x0D和0x0A(分别是\r
和\n
)。
ref:https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/fwrite?view=vs-2017
举例细说
读取图像,通常用opencv,但是考虑到arm上用opencv过于庞大,考虑在pc上把图像的数据读取出来,然后整理下顺序,再用fwrite保存。后面在arm上直接fread就行了,避开了opencv。
但在具体实现的时候发现,fwrite后再fread,只有前面一部分数据是正确的!原因如上面说的,保存到文件的是float数组,但是打开文件的模式错误的设定为了字符模式,而不是二进制模式。
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
string im_pth = "../cat_227.jpg";
IplImage* img = cvLoadImage(im_pth.c_str(), CV_LOAD_IMAGE_COLOR);
int iImgChnl = 3;
int iImgHgt = 227;
int iImgWth = 227;
int num_elem = iImgChnl * iImgHgt * iImgWth;
float* pfImgData;
pfImgData = (float*)malloc(sizeof(float)*num_elem);
float* f_input_data_b = (float*)malloc(sizeof(float)*iImgHgt*iImgWth);
float* f_input_data_g = (float*)malloc(sizeof(float)*iImgHgt*iImgWth);
float* f_input_data_r = (float*)malloc(sizeof(float)*iImgHgt*iImgWth);
for (int i = 0; i < num_elem; i += 3) {
f_input_data_b[i / 3] = (float)(unsigned char)(img->imageData[i]);
f_input_data_g[i / 3] = (float)(unsigned char)(img->imageData[i + 1]);
f_input_data_r[i / 3] = (float)(unsigned char)(img->imageData[i + 2]);
}
for (int i = 0; i < iImgHgt*iImgWth; i++) {
pfImgData[i] = f_input_data_b[i];
}
for (int i = 0; i < iImgHgt*iImgWth; i++) {
pfImgData[i + iImgHgt*iImgWth] = f_input_data_g[i];
}
for (int i = 0; i < iImgHgt*iImgWth; i++) {
pfImgData[i + 2*iImgHgt*iImgWth] = f_input_data_r[i];
}
int ret;
string save_pth = "../cat_227.fread_float.w";
FILE* fout = fopen(save_pth.c_str(), "w");
ret = fwrite((void*)pfImgData, sizeof(float), num_elem, fout);
fclose(fout);
printf("--- pfImgData[5847]=%f, pfImgData[5848]=%f\n", pfImgData[5847], pfImgData[5848]);
//--------------------------------------------------
float* tuopan = (float*)malloc(sizeof(float)*num_elem);
FILE* fin = fopen(save_pth.c_str(), "rb");
ret = fread((void*)tuopan, sizeof(float), num_elem, fin);
fclose(fin);
printf("--- tuopan[5847]=%f, tuopan[5848]=%f\n", tuopan[5847], tuopan[5848]);
printf("--- check here---\n");
return 0;
}
测试环境:VS2013 update5, win32/x64 debug/release模式
调试结果:
发现第5848个元素是错误的。
通过分别设定字符模式和二进制模式来写入文件,看到了差异:
第一次出现差异的地方是0x5B60后的一个元素,0x5B60恰好是十进制下的23392,23392=5848 x 4, 4表示sizeof(float)