例程见银行业务员系统那个编程作业。
fopen
作用:打开文件
函数原型:FILE * fopen(const char * path, const char * mode);
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回 NULL,并把错误代码存在 error 。
例:FILE *fp = fopen("test.txt","r+")
第三个参数:
字符串 说明
r 以只读方式打开文件,该文件必须存在。
r+ 以读/写方式打开文件,该文件必须存在。
w 以只写方式打开文件, 若文件存在则覆盖,文件内容会消失,文件长度清零;不存在则创建该文件。
w+ 以读/写方式打开文件,若文件存在则覆盖,文件内容会消失,文件长度清零;不存在则创建该文件。
a 以附加的方式打开只写文件。若文件不存在,则会创建该文件;如果文件存在,则打开文件时,文件指
针自动定位到文件末尾,即文件原先的内容会被保留(EOF 符保留)。
a+ 以附加方式打开可读/写的文件。若文件不存在,则会创建该文件;如果文件存在,则打开文件时,文
件指针自动定位到文件末尾,加到文件尾后,即文件原先的内容会被保留(EOF符不保留)。
注意:
1. 所有的打开方式后边都可以加b或者t。b(binary):以二进制方式打开;t(text):以文本方式打开。不加时默认为文本方式(t) 例如:rb,rt+,wb.....。
以 x 结尾的模式为独占模式,文件已存在或者无法创建(一般是路径不正确)都会导致 fopen 失败。文件以操作系统支持的独占模式打开。
有些 C编译系统可能不完全提供所有功能,有的C版本不用"r+"、"w+"、"a+",而用"rw"、"wr"、"ar"等。
2. 一般情况下,不要直接使用附加方式打开文件然后操作,因为其附加方式很容易使文件位置混乱;附加方式只使用于一直追加,不对以前的值进行修改的操作。一般用附加方式来创建文件,例(本处暂不判断返回值):
FILE *fp = NULL;
fp = fopen("a.txt", "at+");
fclose(fp);
fp = fopen("a.txt", "rt+")
...
fclose(fp);
前半部分:at+方式打开文件,文件不存在时创建,存在时则什么也不做,然后关闭文件;
后半部分:rt+方式打开文件,前边at+保证了文件是存在的,rt+可以对文件读写。如果用wt+,则会覆盖。
3. 回车问题
文本文件中:windows以“\r\n”代表换行,linux是“\n”,mac是“\r”。
二进制文件:windows以“\n”代表换行,linux是“\n”,mac是“\r”。
Windows下: 若以文本模式打开文件,读写时回车如下操作:
写:如果碰到 '\n',则在要写到文件中时,会在前边加一个'\r',即实际写入文件的是"\r\n",也就是0x0D,0X0A,这在文件中是占两个字节的,用fseek时要注意。
读:如果碰到"\r\n",则只会将'\n'读到缓存中,但是"\r\n"要算两个字节长度。
回车的显示问题:
1. 如果在windows下编辑的文件,按下的回车都会转化为"\r\n",在linux下用vi显示就会有^M。
如果在Linux用vi编辑的文件,按下的回车会转化为'\n',windows下不换行。
2. UltraEdit在写文件时,回车都会转化为"\r\n",但它将'\n'显示换行,但记事本等不会换行。
4. '\0'问题
'\0'仅仅用于方便操作字符串,用其表示字符串结束。将一个字符串写到文件中时,不会有'\0'写进去;从文件中读入字符串缓存时,也不会自动加'\0'。
'\0'自动添加的情况:定义了字符串常量,例如 char *str1 = "hello world";它会保存在只读数据段,后边会自动添加'\0',也就是0x00.
需要手动添加'\0'的情况:想要用到这个'\0'时,例如:用printf函数时,strlen函数时,strncpy时。因为printf和strlen的函数是通过判断'\0'来结束的,strncpy只会拷贝指定长度不会自动添加'\0',strcpy函数可以。
fwrite
作用:向文件中写入数据
函数原型:size_t fwrite(const void *ptr, size_t size, size_t cnt, FILE *stream);
参数:buffer:是一个指针,对fwrite来说,是要获取数据的地址;
size:要写入的单个数据项的字节数;
cnt:要进行写入size字节的数据项的个数;
stream:目标文件指针;
返回值:写入的数据的项数。
成功:返回值等于第三个参数cnt;
失败:返回0
例:struct mystruct
{
int i;
char cha;
}t_buf;
fwrite(&t_buf, sizeof(t_buf), 1, fp);
fread
作用:从文件中读出数据
函数原型:size_t fread(void *ptr, size_t size, size_t cnt, FILE *stream);
参数:buffer:是一个指针,对fwrite来说,是要获取数据的地址;
size:要读出的单个数据项的字节数;
cnt:要读出size字节的数据项的个数;
stream:目标文件指针;
返回值:读出的数据的项数。
成功:返回值小于或者等于第三个参数cnt;
失败:设置errno,返回0
例:fread(&t_buf, sizeof(t_buf), 1, fp);
stat
可以用stat()、fstat()、lstat()获得文件大小,stat()效率最高、也比较稳定。
作用: 将参数file_name所指的文件状态,复制到参数buf所指的结构中
头文件: #include <sys/stat.h> #include <unistd.h>
函数原型:int stat(const char * file_name, struct stat *buf);
返回值: 执行成功则返回0,失败返回-1,错误代码存于errno
示例:
例1
int main(int argc, char **argv)
{
struct stat buf;
stat (“/etc/passwd”, &buf);
printf(“/etc/passwd file size = %d /n”, buf.st_size);
}
例2
int main(int argc, char **argv)
{
struct stat tStat;
int fd;
int ret;
unsigned int size; //文件大小
fd = open("test.text",RDONLY);
ret = fstat(fd, &tStat);
if(ret)
{
return -1;
}
size = tStat.st_size;
}
struct stat内各参数的说明
struct stat
{
dev_t st_dev; /*device:文件的设备编号 */
ino_t st_ino; /*inode */
mode_t st_mode; /*protection:文件的类型和存取的权限 */
nlink_t st_nlink; /*number of hard links:连到该文件的硬连接数目,刚建立的文件值为1 */
uid_t st_uid; /*user ID of owner:文件所有者的用户识别码 */
gid_t st_gid; /*group ID of owner:文件所有者的组识别码 */
dev_t st_rdev; /*device type:若此文件为装置设备文件,则为其设备编号 */
off_t st_size; /*total size, in bytes*/
unsigned long st_blksize; /*blocksize for filesystem I/O */
unsigned long st_blocks; /*number of blocks allocated:占用文件区块的个数,每一区块大小为512个字节*/
time_t st_atime; /* time of lastaccess:文件最近一次被存取或被执行的时间,一般只有在用mknod、utime、read、write与tructate时改变 */
time_t st_mtime; /* time of last modification:文件最后一次被修改的时间,一般只有在用mknod、utime和write时才会改变 */
time_t st_ctime; /* time of last change:i-node最近一次被更改的时间,此参数会在文件所有者、组、权限被更改时更新。 */
};
先前所描述的st_mode则定义了下列数种情况
S_IFMT 0170000 文件类型的位遮罩
S_IFSOCK 0140000 scoket
S_IFLNK 0120000 符号连接
S_IFREG 0100000 一般文件
S_IFBLK 0060000 区块装置
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符装置
S_IFIFO 0010000 先进先出
S_ISUID 04000 文件的(set user-id on execution)位
S_ISGID 02000 文件的(set group-id on execution)位
S_ISVTX 01000 文件的sticky位
S_IRUSR(S_IREAD) 00400 文件所有者具可读取权限
S_IWUSR(S_IWRITE)00200 文件所有者具可写入权限
S_IXUSR(S_IEXEC) 00100 文件所有者具可执行权限
S_IRGRP 00040 用户组具可读取权限
S_IWGRP 00020 用户组具可写入权限
S_IXGRP 00010 用户组具可执行权限
S_IROTH 00004 其他用户具可读取权限
S_IWOTH 00002 其他用户具可写入权限
S_IXOTH 00001 其他用户具可执行权限
上述的文件类型在POSIX中定义了检查这些类型的宏定义
S_ISLNK (st_mode) 判断是否为符号连接
S_ISREG (st_mode) 是否为一般文件
S_ISDIR (st_mode)是否为目录
S_ISCHR (st_mode)是否为字符装置文件
S_ISBLK (s3e) 是否为先进先出
S_ISSOCK (st_mode) 是否为socket
若一目录具有sticky 位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名。
fputc
作用:写一个字符到文件里
原型:int fputc (int c, File *fp)
参数:c为输出的字符量,虽然函数被定义为整型数,但仅用其低八位;fp为文件指针
返回值:成功:写入文件的字符的ASCII码值
出错:EOF(-1)。
当正确写入一个字符或一个字节的数据后,文件内部写指针会自动后移一个字节的位置。
EOF是在头文件 stdio.h中定义的宏。
fputc和putc区别:
putc()与fputc()作用相同,但putc()为宏定义,非真正的函数调用
fclose
作用:关闭文件
函数原型:int fclose( FILE *fp );
返回值:如果流成功关闭,fclose 返回 0,否则返回EOF(-1)。
(如果流为NULL,而且程序可以继续执行,fclose设定error number给EINVAL,并返回EOF。)
例:ret = fclose(fp);
fseek/lseek
作用:设置文件指针位置。注意:\r和\n都会占一个字节的位置。
函数原型:int fseek(FILE *stream, long offset, int fromwhere)
返回值:
成功:函数返回0。 stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,。
失败::(比如offset取值大于等于2*1024*1024*1024,即long的正数范围2G),则不改变stream指向的位置,
函数返回一个非0值,errno存放错误码。
此函数第三个参数: SEEK_SET:文件开头 SEEK_CUR: 当前位置 SEEK_END: 文件结尾
例1:fseek(fp, 0, SEEK_SET) //跳转到首位置
例2:fseek(fp, 0, SEEK_END) //跳转到文件结尾
例3:fseek(fp, 10, SEEK_CUR) //跳转到当前位置向后偏移10个字节的位置
ftell(fp) //得到文件位置指针当前位置相对于文件首的偏移字节数
fseek函数和lseek函数类似,只是返回值不同:
lseek 返回off_t(偏移值,即长度),而fseek返回的是一个整型(执行的结果)。
lseek成功:返回目前的读写位置, 也就是距离文件开头多少个字节
失败:返回-1, errno 会存放错误代码.
附加说明:Linux 系统不允许lseek()对tty 装置作用, 此项动作会令lseek()返回ESPIPE.
fprintf
作用:以指定格式向文件写入
函数原型:int fprintf (FILE* stream, const char*format, [argument])
返回值:输出的字符数,发生错误时返回一个负值
例:fseek(fp,46*i,SEEK_SET);
fprintf(fp,"%-10s",pwd);
fgetc
作用:从文件中读一个字节
函数原型:int fgetc(FILE *stream);
返回值:
成功:返回所读取的一个字节。
如果读到文件末尾或者读取出错:返回EOF。
例:char ch;
ch = fgetc(fp)
fscanf
作用:以指定格式从文件读出。遇到空格或换行时结束。
函数原型:int fscanf(FILE*stream, constchar*format, [argument...])
返回值:整型,成功返回读入的参数的个数,失败返回EOF(-1)。
例:fseek(fp,46*i,SEEK_SET);
fscanf(fp,"%d",&id);
fgets
作用:从文件中读出字符串或者从屏幕上输入字符串。遇到空格不结束,遇到换行结束。
函数原型:char *fgets(char *s, int size, FILE *stream);
返回值:
成功:返回第一个参数s;在读字符时遇到end-of-file,则eof指示器被设置
失败:没读入任何字符就遇到end-of-file,则buf保持原来的内容,返回NULL;
发生读入错误,error指示器被设置,返回NULL,buf的值可能被改变。
例1:fgets(buf,sizeof(buf),fp)
例2:fgets(buf,sizeof(buf),stdin)
详解:从fp所指文件中读入n-1个字符放入str为起始地址的空间内;
如果在未读满n-1个字符之时,已读到一个换行符或一个EOF(文件结束标志),
结束本次读操作,读入的字符串中最后包含读到的换行符(这就是为什么第二个
参数是n,而最多只能读入n-1个字符的原因,这个地方一定要注意)
ftell
作用:得到当前文件位置指针相对于文件首的偏移字节数
函数原型:long ftell(FILE *stream);
返回值:当前文件位置指针相对于文件首的偏移字节数
该函数对大于231-1文件,即:2.1G以上的文件操作时可能出错。
例:fseek(fp, 0, SEEK_END) //跳转到文件结尾
ftell(fp) //得到文件位置指针当前位置相对于文件首的偏移字节数
feof
作用:检测流上的文件结束符
函数原型:int feof(FILE *stream);
返回值:
非0值:文件结束
0值:其他情况
例:int c;
while((c=fgetc(fp)) != EOF)
{
printf("%X/n", c);
}
feof和eof的区别
两个的定义(stdio.h中)
#define EOF (-1)
#define _IOEOF 0x0010
#define feof(_stream) ((_stream)->_flag & _IOEOF)
(1)EOF是文本文件结束的标志。文本文件中,数据是以字符的ASCⅡ代码值的形式存放,
普通字符的ASCⅡ代码的范围是32到127(十进制),EOF的16进制代码为0xFF(十进制为-1),
因此可以用EOF作为文件结束标志。
(2)EOF不是只能用于文本文件,要看定义的变量的类型。
例程1:
下面这段程序对文本文件和二进制文件都可以:
int c;
while((c=fgetc(fp)) != EOF)
{
printf("%X/n", c);
}
如果读到了FF,由于c定义为int型,所以实际上c=0x000000FF,
不等于EOF(-1=0xFFFFFFFF),因此不会误判为文件结尾。
例程2:
如果把c定义为char类型,就有可能产生混淆了。
char c;
while((c=fgetc(fp)) != EOF)
{
printf("%X/n", c);
}
因为文本文件中存储的是ASCII码,而ASCII码中FF代表空值(blank),一般不使用,
所以如果读文件返回了FF,说明已经到了文本文件的结尾。但是如果是二进制文件,
其中可能会包含FF,因此不能把读到EOF作为文件结束的条件,此时只能用feof()函数。
例程3:
VC中,只有当文件位置指针(fp->_ptr)到了文件末尾,然后再发生读/写操作时,
标志位(fp->_flag)才会被置为含有_IOEOF。然后再调用feof(),才会得到文件结束的信息。
因此,如果运行如下程序:
char c;
while(!feof(fp))
{
c = fgetc(fp);
printf("%X/n", c);
}
会发现多输出了一个FF,原因就是在读完最后一个字符后,fp->flag仍然没有被置为_IOEOF,
因而feof()仍然没有探测到文件结尾。直到再次调用fgetc()执行读操作,feof()才能探测到
文件结尾。这样就多输出了一个-1(即FF)。
正确的写法应该是:
char c;
c = fgetc(fp);
while(!feof(fp))
{
printf("%X/n", c);
c = fgetc(fp);
}