C語言fread/fwrite填坑記


坑的描述

fwrite把數據寫入文件,再用fread讀取,發現后半部分的數據可能是錯的。

原因:原本要寫入文件的數據中,有0x0A,如果用的是文本模式打開的文件流,在windows下0x0A會被轉換為0x0D和0x0A

其實windows下的git bash每次git add后都有類似的提示,只是一直沒太注意:

先說結論

freadfwrite的時候,如果是要寫入字符,那么打開的文件、讀取的文件,用字符模式(wr

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數組等,則一定要用二進制模式打開文件(wbrb)(盡管在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;
}

cat_227.jpg

測試環境:VS2013 update5, win32/x64 debug/release模式

調試結果:

監視變量

發現第5848個元素是錯誤的。

通過分別設定字符模式和二進制模式來寫入文件,看到了差異:
對比二進制文件

第一次出現差異的地方是0x5B60后的一個元素,0x5B60恰好是十進制下的23392,23392=5848 x 4, 4表示sizeof(float)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM