Linux 应用开发_04 文件流与目录流管理
本课目标
(1)编程目标:
(a)实现磁盘文件的拷贝操作。
(b)缓冲区类型对磁盘真正写入操作的影响。
(2)理解文件流操作以及缓冲区概念。
(3)掌握ansi c 文件流相关操作函数。
(4)理解与掌握目录流操作。
主要知识点
(1)流的基本原理及作用,与文件描述符的关系。标准输入输出流基本概念,文件流结构体。
(2)缓冲区类型以及缓冲区作用。如何修改缓冲区。setbuf,setvbuf
(3)文件流操作。
(4)流的效率与可移植性。
(5)目录流操作与编程。
课程内容
文件流概念
FILE *fp = fopen();
typedef struct _IO_FILE FILE;
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest isflags. */
#define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambufprotocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fieldsdirectly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing upand undo. */
char *_IO_save_base; /* Pointer to start of non-currentget area. */
char *_IO_backup_base; /* Pointer to first validcharacter of backup area */
char *_IO_save_end; /* Pointer to end of non-current getarea. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno; //关联的文件描述符
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset butit's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
流是在用户空间在文件描述符基础上进行的封装,添加了如下信息,使功能更强。
(1)缓冲区
(2)读写位置。
流的一个重要提高效率的方法是使用缓冲区,将多次的系统调用合并成一次系统调用,节约执行的
时间。
流和缓冲区:
(1)全缓冲。只有当数据量达到某个限制后(不同的平台有差异4096),或者主动的要求刷新缓冲
区(fclose/fflush),才真正执行一次系统调用操作。常见的哪些流是全缓冲:文件流,即以fopen 打开的
文件。
(2)行缓冲。只有当数据量达到某个限制(128,1024)或者遇到换行,或者主动要求(fclose/fflush)
刷新缓冲区后,才真正执行一次系统调用操作。常见的是终端命令行。
printf(“step 1\n”);
printf(“step 2\n”);
(3)无缓冲。需要实时显示信息的,例如标准的错误输出。fprintf(stderr,);
默认的三个打开的文件对应的流
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream.*/
extern struct _IO_FILE *stdout; /* Standard outputstream. */
extern struct _IO_FILE *stderr; /* Standard error outputstream. */
如果要修改缓冲的类型或者自己指定对应缓冲区的位置。可以使用 setbuf,setvbuf
#include <stdio.h>
void setbuf(FILE *stream, char *buf);
void setbuffer(FILE *stream, char *buf, size_t size);
void setlinebuf(FILE *stream);
int setvbuf(FILE *stream, char *buf, int mode, size_tsize);
_IONBF unbuffered
_IOLBF line buffered
_IOFBF fully buffered
通过程序来验证缓冲区的问题:
文件流的操作:编程中用到的函数,ANSI C 库函数。open/read/write/close 是系统调用函数。
1.打开/关闭
fopen
fclose
FILE *fopen(const char *path, const char *mode);
第一个参数为打开文件的路径(字符串),第二参数为打开的方式(字符串)。
r:只读。open O_RDONLY
w:只读。 open->O_WRONLY|O_CREAT|O_TRUNC
a:追加。open->O_APPEND|O_CREAT|O_WRONLY
X+:可读可写。open O_RDWR
FILE *fdopen(int fd, const char *mode);//将一个文件描述符封装成一个流
FILE *freopen(const char *path, const char *mode, FILE*stream);
使用完成后,全用flose 关闭这个流对象,期间会刷新流的缓冲区。
2、读写
一个字符一个字符的读写
fgetc/fputc
int fgetc(FILE *stream); //返回的是这个字符的sacii 值
int getc(FILE *stream); //类似于fgetc
int getchar(void);//从标准的输入流中读取一个字符。返回其Ascii 值
int ungetc(int c, FILE *stream);
int fputc(int c, FILE *stream); //将c 写入到stream流所指向的文件中
int putc(int c, FILE *stream);
int putchar(int c); //将c 字符写入到标准的输出流。
一行一行的读写
char *fgets(char *s, int size, FILE *stream);
//从stream 流中期望读取大小为 size 个byte 的字符串,存储在s 指向的内存空间中,返回字符的位
置。
int fputs(const char *s, FILE *stream);
将s 指向的内存空间字符串写入到stream流中,返回写入的数据量。
char *gets(char *s); //从标准的输入流读取一行。
int puts(const char *s); //将一行字符串写入到标准的输出流中。
任意的大小读写
fread/fwrite
size_t fread(void *ptr, size_t size, size_t nmemb, FILE*stream);
期望从stream 流所指向的文件读取nmemb 个大小为size 的数据,并存储在ptr 所指向的内存空间
中。返回是真正读出的个数。
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
将存储在ptr 开始的内存空间中 nmemb 个大小为size 的数据写入到stream 流所指向的文件。
强调一下,read 函数返回的和write 返回的值是真正读写的字节数,而fread/fwrite 返回的是真正读
写的个数。字节数= size* nmemb。
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
3. 文件流状态的检测和设置
(1)读写出错的状态:
void clearerr(FILE *stream);
int ferror(FILE *stream);
如果读写出错,ferror 返回非0 值,可以全用clearerr 函数清除这个错误。
(2)读写结束的状态:feof(stream);
int feof(FILE *stream);,如果流的读写位置已经在文件尾部,返回非0 值.
文件流的读写位置
在流读写相关函数操作流时,文件的读写位置会自动的跟着移动。
文件的读写位置有三个参考点
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);//返回当前的读写位置距离文件头的字节数。
void rewind(FILE *stream); //将文件读写位置置到文件开头。
int fgetpos(FILE *stream, fpos_t *pos);
int fsetpos(FILE *stream, fpos_t *pos);
4. 写一个简单拷贝程序,从一个文件拷贝到另一个文件,分别用字节读,行读,任意大小读的方
式。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])//argv[1]:src file,argv[2]:dst file
{
if(argc!=3)
{
printf("pls usage:%s src_filedst_file\n",argv[0]);
exit(EXIT_FAILURE);
}
FILE *fp_src = fopen(argv[1],"r");
if(NULL == fp_src)
{
printf("fopen error\n");exit(EXIT_FAILURE);
}
FILE *fp_dst = fopen(argv[2],"w");
if(NULL == fp_dst)
{
printf("fopen dst error\n");exit(EXIT_FAILURE);
}
/*
char ch;
while(1)
{
ch = fgetc(fp_src);
if(feof(fp_src))
{
break;
}
fputc(ch,fp_dst);
}
char buf[128];
while(1)
{
memset(buf,'\0',128);
fgets(buf,127,fp_src);
fputs(buf,fp_dst);
if(feof(fp_src))
break;
}
fclose(fp_src);
fclose(fp_dst);
}
5. 流的格式化输入输出的问题
printf/scanf 这一类函数的实现相当的复杂。
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format,...);
返回值:返回真正打印字符的字节数。
printf 是fprintf 的特例。
int fprintf(FILE *stream, const char *format, ...);
将列表中的数据以指定的格式format 写入到stream 所指向的流中。printffprintf(stdout,format….);
格式化串的标准格式:具体可以参阅手册。
%[标志 ] [最小字段宽度] .[精度 ] [参数长度] [转换类型]
标志
-:表示左对齐
+
空格
#
0
最小字段宽度表示输出数据最小宽度,如果不够,空格补充。
精度表示最少输出数据的位数,浮点数转为小数后最小位数,字符串转换后的最大字符数。
参数长度
hh
h
l
l
L
转换类型:
d
o
u
x
f
e
c
s
p
int scanf(const char *format, ...);是fscanf 的特例,fscanf(stdio,format,,)
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
可变参数为存放数据的地址列表。
format 串的格式是
%[*][最大宽度][转换类型]
[*]抑制转换,按这个格式输入但不存入后面的内存空间。
sprint/sscanf 将数据输入/输入到字符串中。