/***************************************************************************************************
文件基本操作
编译环境:VS2005
编译结果:通过
张永辉 2012年9月19日
***************************************************************************************************/
目录:
一:文件概述
二:打开文件
三:读写字符
四:读写字符串
五:格式化数据读写
六:文件数据块读写
一:文件概述
文件
所谓文件(file)一般指存储在外部介质上数据的集合.这些数据可以是有规则的集合,也可以是无序的集合。
操作系统也是以文件为单位对数据进行管理的,要访问外部介质上的数据,必须先按照文件名进行查找, 然后从该文了解了文件及文件存储形式件中读取数据。
要想写数据到外部介质,必须得建立一个文件,然后再写入。
二进制文件和ASCII文本文件
文本文件方式存储多的是字符(ASCII码),比如一个整数10000,类型是short,占2字节,存储文本形式将占用5个字节,一共5个字符。
二进制文件方式多用于直接将内存里面的数据形式原封不动存放到文件里,比如上面的short 10000,在内存中占2字节,存储内容为10000的二进制数,存到文件后还是占2字节,内容也是10000的二进制。
这种方式可以整块数据一块儿存储,同时还可以将内存数据映射到文件里。
C对文件的操作形式:
C语言操作文件可以是字节流或者二进制流。把数据看成是一连串字节,而不需要考虑边界。
C语言对文件的存取是以字节为单位的。输入输出数据流的开始和结束仅受程序控制。
缓冲文件系统
指系统在内存区为正在使用的文件开辟一个缓冲区,输入或输出数据都要先送到缓冲区,装满后再一起送到磁盘去。反向亦是。
在缓冲文件系统中,每个使用的文件都会在内存中开辟一个区,用于存放文件的有关信息,这些文件信息就保存在一个结构体变量中的,名为FILE,VC2005在stdio.h下FILE的定义如下:
struct _iobuf
{
char *_ptr; // 指向buffer中第一个未读的字节
int _cnt; // 记录剩余未读字节的个数
char *_base; // 指向一个字符数组,即这个文件的缓冲
int _flag; // FILE结构所代表的打开文件的一些属性
int _file; // 用于获取文件描述
int _charbuf; // 单字节的缓冲,即缓冲大小仅为1个字节,如果为单字节缓冲,_base将无效
int _bufsiz; // 记录这个缓冲的大小
char *_tmpfname; //
};
typedef struct _iobuf FILE;
_flag就标记了缓冲的信息
#define _IOYOURBUF 0x0100 // 使用用户通过setbuf提供的buffer
#define _IOMYBUF 0x0008 // 这个文件使用内部的缓冲
#define _IONBF 0x0004 // 无缓冲模式
#define _IOLBF 0x0040 // 行缓冲模式
#define _IOFBF 0x0000 // 全缓冲模式
_flag也标记了读写模式,比如"r+"、"w+"等。
#define _IOREAD 0x0001 // 只读
#define _IOWRT 0x0002 // 只写
#define _IORW 0x0080 // 可读可写
缓冲区 避免频繁的系统调用开销,不需要每次都访问实际的文件。
也有不足,比如写文件时,先是到缓冲,如果此时系统崩溃或者进程意外退出时,有可能导致文件数据的丢失。提供了以下函数来弥补:
int fflush ( FILE* stream ) // flush指定文件的缓冲,若参数为NULL,则flush所有文件的缓冲。
int setvbuf( FILE *stream, char* buf, int mode, size_t size ) // 设定缓冲类型,如上面的表格。
void setbuf( FILE* stream, char* buf ) // 设置文件的缓冲,等价于( void )setvbuf( stream, buf, _IOFBF, BUFSIZ ).
flush一个缓冲:是指对写缓冲而言,将缓冲内的数据全部写入实际的文件,并将缓冲清空,避免文件丢失。
全缓冲:除了用户手动调用fflush外,仅当缓冲满的时候,缓冲才会被自动flush掉。
行缓冲:这种缓冲仅用于文本文件,在输入输出遇到一个换行符时,缓冲就会被自动flush,因此叫行缓冲。
二:打开文件 //--------------------------文件打开操作,VS2005编译通过-------------------------------
#include <stdio.h>
int main( void )
{
FILE* pReadFile = fopen( "d:/filetest/1.txt", "w+" ); // 打开文件
if ( pReadFile == NULL )
{
return 0;
}
fclose( pReadFile ); // 关闭文件
return 0;
}
函数fopen(...) 第一个参数是文件路径,第二个参数是读写模式,返回值为0表示打开失败。
"r" (只读) 为输入打开一个文本文件, 不存在则失败
"w" (只写) 为输出打开一个文本文件, 不存在则新建,存在则删除后再新建
"a" (追加) 向文本文件尾部增加数据, 不存在则创建,存在则追加
'rb" (只读) 为输入打开一个二进制文件, 不存在则失败
"wb" (只写) 为输入打开一个二进制文件, 不存在则新建,存在则删除后新建
"ab" (追加) 向二进制文件尾部增加数据, 不存在则创建,存在则追加
"r+" (读写) 为读写打开一个文本文件, 不存在则失败
"w+" (读写) 为读写建立一个新的文本文件,不存在则新建,存在则删除后新建
"a+" (读写) 为读写打开一个文本文件, 不存在则创建,存在则追加
"rb+"(读写) 为读写打开一个二进制文件, 不存在则失败
"wb+"(读写) 为读写建立一个新的二进制文件不存在则新建,存在则删除后新建
"ab+"(读写) 为读写打开一个二进制文件, 不存在则创建,存在则追加
三:读写字符 //--------------------------VS2005编译通过--------------------------------------
C语言为提供了两个函数:
int __cdecl fgetc( FILE* stream ); // 从文件读入一个字符
int __cdecl fputc( int ch, FILE* stream ); // 写入一个字符到文件
#include <stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
char cInput;
int i = 0;
char szOutput[ 32 ] = "name/nzyh";
FILE* pReadFile = fopen( "d:/filetest/1.txt", "r+" );// 打开文件,追加。
if ( pReadFile == NULL )
{
return 0;
}
while ( ( cInput = fgetc( pReadFile ) ) != EOF ) //从文件读入一个字符,直到尾部,则返回EOF(-1)。
{
printf( "%c", cInput ); //遇到回车符('/r')为13,换行符('/n')为10,printf输出也是正常的。
}
i = 0;
while ( szOutput[i] != '\0')
{
fputc( szOutput[i], pReadFile ); // 写入一个字符到文件,字符串中的'/n' 原样写入到文件了
i++;
}
fputc( 10, pReadFile ); //写入换行 这样不行fputc( '\n', pReadFile );
fputc( 'x', pReadFile );
fputc( 13, pReadFile ); //写入回车
fputc( 'y', pReadFile );
fclose(pReadFile); // 关闭文件
return 0;
}
同时,到这里想到第一个问题,我们又来观察一下,当刚使用fopen函数时,pWriteFile里面的内容是:
pWriteFile 0x00437bb0
_ptr 0x00000000
_cnt 0
_base 0x00000000
_flag 2
_file 3
_charbuf 0
_bufsiz 0
_tmpfname 0x00000000
而执行了fputs函数,到换行符后我们再看pWriteFile里面的内容:
pWriteFile 0x00437bb0
_ptr 0x00385019
_cnt 4087 还剩下多少个字节没有写,全缓冲模式,填充4096字节后自动会写入到文件
_base 0x00385010 指向缓冲区
_flag 10
_file 3
_charbuf 0
_bufsiz 4096
_tmpfname 0x00000000
同样,如果你想立即将缓冲区的数据写到文件里,可以在fclose函数前面加上fflush( pWriteFile );
当执行完此函数后,数据便写进了文件,最后再关闭文件。
四:读写字符串 //--------------------------VS2005编译通过-------------------------------------------
C语言提供了2个函数:
char* __cdecl fgets( char* _Buf, int _MaxCount, FILE* _File );
参数一:要从文件中读入字符串的存放空间。
参数二:最大读取字节数。
参数三:文件指针。
返回值:读入的字符串指针。
备 注:遇到换行或者最大个数时将返回, 只能读到(_MaxCount-1)个字符,最后会添加'\n'
int __cdecl fputs( const char* _Str, FILE* _File );
参数一:要写入文件的字符串
参数二:文件指针
返回值:失败或成功,0表示成功,其它表示失败。
#include <stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
char szInput[ 32 ] = { 0 };
char *szOutput = "12345678";
char* pRet = NULL;
FILE* pReadFile = fopen( "d:\\filetest\\1.txt", "r+" ); // 打开文件
if ( pReadFile == NULL )
{
return 0;
}
pRet = fgets( szInput, 32, pReadFile ); //从文件中读取一个字符串到szInput数组中
printf("read string = %s \n" , szInput);
fputs( szOutput, pReadFile ); //写入一个字符串到文件
fclose( pReadFile ); // 关闭文件
return 0;
}
五:格式化数据读写//--------------------------VS2005编译通过----------------------------------------
相关函数:
int __cdecl fprintf( FILE* _File, const char* _Format, ... ); //向文件写入
int __cdecl fscanf( FILE* _File, const char* _Format, ... ); //从文件读出
这两个函数跟printf和scanf的用法非常相似,只是这里输入输出是关于文件的。
#include <stdio.h>
typedef struct SStudent
{
int number;
char name[ 11 ];
}Student;
int main( void )
{
Student stu1;
Student stu2;
FILE* pReadFile = fopen( "d:\\filetest\\1.txt", "r+" ); // 打开文件
if ( pReadFile == NULL )
{
return 0;
}
stu1.number = 100;
strcpy(stu1.name, "zhangfei");
//不会把结束符'\0'写到文件里 所以使用fputc写入换行符号
fprintf( pReadFile, "%d %s", stu1.number, stu1.name );
fputc( 10, pReadFile );
stu1.number = 101;
fprintf( pReadFile, "%d %s", stu1.number, stu1.name );
fflush(pReadFile); //立即写入到文件
//写入了如下内容
//100 zhangfei
//101 zhangfei
rewind(pReadFile); //将文件指针移动到文件开始部分,否则读不到内容
//fscanf读取数据是以空格、制表符、换行符进行分割的
fscanf( pReadFile, "%d %s", &stu2.number, &stu2.name );
printf("number = %d name = %s \n",stu2.number, stu2.name);
fclose( pReadFile ); // 关闭文件
return 0;
}
六:文件数据块读写//--------------------------VS2005编译通过----------------------------------------
同样C语言也提供了两个函数:
size_t __cdecl fwrite
(
const void *buffer, // 要写入文件的数据块
size_t size, // 写入文件的字节数
size_t count, // 写入count个size大小的数据
FILE *stream // 文件指针
);
size_t __cdecl fread
(
void * _DstBuf, // 存放从文件读出来的数据
size_t _ElementSize, // 读取字节数
size_t _Count, // 读入次数
FILE * _File // 文件指针
);
#include <stdio.h>
typedef struct SStudent
{
int number;
char name[12];
}Student;
int main( void )
{
Student stu1;
FILE* pWriteFile = fopen( "E://mytest.txt", "r+" ); // 打开文件
if ( pWriteFile == NULL )
{
return 0;
}
stu1.number = 10000;
strcpy( stu1.name, "masefee");
fwrite( &stu1, sizeof( stu1 ), 1, pWriteFile ); //被写入“ ' masefee 烫烫 ” 二进制“10 27 00 00 6d 61 73 65 66 65 65 00 cc cc cc cc”
rewind(pReadFile);
fread( &stu2, sizeof( stu2 ), 1, pWriteFile ); //这样就能填充好stu2结构体变量了 stu2.number=0x00002710即10000
fclose( pWriteFile ); // 关闭文件
return 0;
}
注:字符串系统默认初识化为0xcc,0xcc是汇编中断指令的机器码,主要防止访问越解释,进行中断报错。而0xcccc就是中文编码的“烫”字。
数据的对齐很重要,比如bmp、jpg、exe、dll等文件都是由很多个数据块,直接写入文件的,这样文件头记录了很多偏移,很多大小等就显得非常重要了。