在 Windows 平台上,读写磁盘文件是相当多应用程序经常会涉及到的一种功能。该主题涉及到采用 C/C++/MFC/Win32(API) 中提供的接口函数来操作磁盘文件的方法,以及其中需要注意的地方。
 
=============================================================
0.      磁盘文件数据存储方式
在介绍各种操作文件方式之前,需要先介绍磁盘上文件数据的组织方式。
实际上,文件是在计算机内存中以二进制表示的数据在外部存储介质上的另一种存放形式。 文件通常分为二进制文件和文本文件。 二进制文件是包含在 ASCII 及扩展 ASCII 字符中编写的数据或程序指令的文件。一般是可执行文件 (Exe) 、图形、图像、声音等文件。而文本文件 ( 通常也成为 ASCII 文件 ) ,它的每一字节存放的是可表示为一个字符的 ASCII 代码的文件。这里把文件区分为二进制文件和文本文件,但实际上它们都是以二进制数据的方式来存储的。文本文件里所存储的每一个字节都可以转化为一个 可读 的字符。譬如 ’ a ’ , ’ b ’ , 但在内存中并不会存储' a' , ' b' ,而是存储它们的 ASCII 码: 61 和 62 。
 
1.      C 语言对文件操作的支持
在 C 语言中,对于文件的操作都是利用 FILE 结构体来进行的。包括文件的打开、文件的读写、文件的关闭、文件指针的定位等。
(1)   文件的打开
文件的打开需要用 fopen 函数。该函数带 2 个参数,返回值为指向之前定义的 FILE 结构体指针。语法定义为
FILE* fopen(const char* filename, const char* mod);
参数 1 :表示要打开文件的完整路径,例如 : “C:\\Test\\Zero_Test.txt”.
参数 2 :打开文件的方式。取值为下表所示。
文件打开模式
意义
r/rb
为读取而打开。如果文件不存在或不能找到,函数调用失败。
w/wb
为写入而打开一个空文件。如果给定的文件已经存在,则清空其内容。
a/ab
为写入而打开一个文件。如果文件存在,则在文件尾部添加新数据,在写新数据之前不会移除原有的 EOF 标志。如果文件不存在,则新建一个空文件以待写入。
r+/rb+
打开文件用于写入操作和读取操作,文件必须存在。
w+/wb+
为写入操作和读取操作打开一个空文件。如果文件已经存在,则清空其内容。
a+/ab+
打开文件用于读取操作和添加操作。并且添加操作在添加新数据之前会移除该文件中已有的 EOF 标志,然后当写入操作完成之后再恢复 EOF 标志。如果指定文件不存在,那么首先将新建一个文件。
在上表中,没有带 b 的模式表示打开的是文本文件,而带 b 的模式表示打开的是二进制文件。
通常在定义结构 FILE 时会将其初始化为 NULL 。在打开文件将函数 fopen 的返回值赋值给它。之后需要判断文件是否成功打开,可采用如下方式:
if ((pFile = fopen(“C:\\Test\\Zero_Test.txt”, “w”) == NULL)
   Print(“Opening File Error.”);    // FILE* pFile = NULL;
else
   正常写入数据到文件 …
 
( 2 )文件的关闭
在使用完一个文件之后应该关闭它,以防止它再被误用。“关闭”就是使文件指针变量不再指向该文件,使其脱钩。函数使用方式 :
fclose(pFile);
需要注意的是 fclose 的参数必须是有效的文件指针变量,否则运行时会挂掉。
应该养成在文件数据操作完成后关闭文件的习惯,如果不关闭文件将会丢失数据。同时,在向文件写数据时,是现将数据输出到缓冲区,待缓冲区充满后才正式输出给文件。如果程序运行结束而缓冲区并未充满,则缓冲区中的数据将会丢失。因此利用函数 fclose 来关闭文件可以避免这个问题。
当然,还有另外一个函数 fflush ,也可以用来将缓冲区里的数据输出到文件中。函数调用方式 :
fflush(pFile);
 
  ( 3 )文件的读写
当涉及到大量数据的读写时,可以采用函数 fread 和 fwrite 。 这两个函数通常用于二进制文件的读写。 函数调用方式如下:
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
buffer 是一个指针,是用来存放数据的变量指针,例如内置类型变量的指针,结构体变量指针等。
size 是要读写的字节数。 特别要注意的是,此处并不是数组的大小,结构体内变量的个数等。最好采用 sizeof 操作符来求得 buffer 的内存字节数。
count 是 size 大小的重复次数。
fp 是文件指针。
 
对于这样的结构体:
typedef struct {
    int clr_sel;             // color mode 的选择
    int fixval_sel;          // fixed value 的选择
    float thres_val;         // threshold value 的确定
    float thres_scrlbar_pos; // threshold scroll bar 位置的确定
    int method_sel;          // analysis method 的选择
    int usediff_sel;         // use difference of MSA/TSE 方式的选择
    int usescat_sel;         // use MSA/TSE of scatter signal 方式的选择
    int remxtalk_sel;        // remove crosstalk 方式的选择
    float timewin_scrlbar_pos; // time window scroll bar 位置的确定
    int timewin_sel;         // time window 模式的选择
    int freq_sel;            // frequency selection 模式的选择
    int path_sel;            // path selection 模式的选择   
} IMG_SETTINGS;     
       IMG_SETTINGS m_img_settings;
文件读写操作时,可采用如下方式:
fwrite(&m_img_settings, sizeof(m_img_settings), 1, pwFile);
fread(&m_img_settings, sizeof(m_img_settings), 1, prFile);
====================================================
 
如果想读写特定格式的文件,可采用 fprintf 和 fscanf 函数。 这两个函数通常是针对文本文件进行操作。
函数调用方式如下:
fprintf(pFile, “%d…”, i, j, k …);
fscanf(pFile, “%d…”, i j, k…);
格式化时可以规定读写数据的精度,类型,以及数据之间的分割符等。
例如:
fprintf(pFile, " %d %d %d %d %d %d %d %d \n", sigSize, samp_points, samp_rate,40, avrag_num, 0, 0, 0);
fprintf(pFile, "%d %.2f %s %d %d %d %d \n", vpathdef[i].frequency1/1000,
                                   vpathdef[i].amplitude, sig_type.c_str(),     psnset->sensorArray[vpathdef[i].actuator-1].channel-tol,
                     psnset->sensorArray[vpathdef[i].sensor-1].channel-tol,   vpathdef[i].gain, 30);  
特别需要注意的是,如果写入的数据的类型相同时,不可为了简便,将格式化的字符串写成如此形式 (.., ”%d”,i,j,k…); // i,j,k… 同整型类型。
如果这样操作的话,读写的数据便只有数据 i 了。
 
最后针对一个面试题来对 C 语言操作文件的方式作一个总结 .
面试题:给你一个整数,例如 12345 ,将这个整数保存到文件中,要求在以记事本打开该文件时,显示的是 :12345 。
给出三种代码:
(1)    代码 1
FILE* pwFile = NULL;
pwFile = fopen(“c:\\Test.txt”, “w”); // create and open file with text mode.
int i = 12345;                       // However, write number with binary mode.
fwrite(&i, 4, 1, pwFile);      // sizeof(int) = 4.
fclose(fwFile);
========================
(2)    代码 2
FILE* pwFile = NULL;
pwFile = fopen(“c:\\Test.txt”, “w”);
char ch[5] = {1+48, 2+48, 3+48, 4+48, 5+48 };
fwrite(ch, 1, 5, pwFile);      // sizeof(char) = 1, 5*sizeof(char) = 5
fclose(pwFile);
=======================
(3)    代码 3
FILE* pwFile = NULL;
pwFile = fopen(“c:\\Test.txt”, “w”);
int i = 12345;
char ch[5];
itoa(i, ch, 10);        // transform int number to string
 
fwrite(ch, 1, 5, fwFile);
fclose(pFile);
==============================
(4)    代码 4
FILE* pwFile = NULL;
pwFile = fopen(“c:\\Test.txt”, “w”);
int i = 12345;
fprintf(pwFile, “%d”, i);   // write number with specified format.
fclose(pwFile);
==============================
 
将 4 种代码在 vc 中进行编译运行,可以发现只有方式( 1 )不满足题目要求 。