坑的描述
用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
)。
The fwrite function writes up to count items, of size length each, from buffer to the output stream. The file pointer associated with stream (if there is one) is incremented by the number of bytes actually written. If stream is opened in text mode, each linefeed is replaced with a carriage-return - linefeed pair. The replacement has no effect on the return value.
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)