Linux文件I/O之二

                             每日一结

一 文件I/O 和标准I/O 

相同点:

都可以实现对文件操作  

不同点:

<1>缓存 

文件I/O没有缓存,直接和系统调用关联

标准I/O有缓存,它的内部调用了系统调用接口 

<2>操作对象 

文件I/O操作对象是文件描述符 

标准I/O操作对象是流 

<3>操作函数 

文件I/O : open ,read ,write,lseek,close 

标准I/O : fopen/freopen/fdopen , fgetc/fgets/fread ,fputc/fputs/fwrite,fseek/rewind/ftll,fclose 

 

二 文件I/O的操作函数 

1.打开文件 

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

 

参数:

@pathname  文件名 

@flags     打开的标志

O_RDONLY   只读 

O_WRONLY   只写 

O_RDWR     读写 

O_CREAT    文件不存在则创建,此时需要指定文件的权限 

O_TRUNC    文件存在则清空文件 

O_APPEND   追加方式 

 

注意:这些宏可以通过"|"一起来使用 

 

"r"    -> O_RDONLY 

"r+"   -> O_RDWR 

"w"    -> O_WRONLY | O_CREAT |O_TRUNC,0666

"w+"   -> O_RDWR | O_CREAT |O_TRUNC,0666

"a"    -> O_WRONLY | O_CREAT |O_APPEND,0666 

"a+"   -> O_RDWR | O_CREAT |O_APPEND,0666 

 

@mode 指定权限(新建一个文件时,默认的权限)

文件实际的权限 mode & ~(umask)

 

返回值:

成功返回文件描述符号, 失败返回-1 


 

-----------------------------------------------------------

文件描述符号分配规则: 最小未使用的文件描述符

0

1

2

若close(1),关闭的是标准输出

则open 获得是1 

-----------------------------------------------------------

练习:

(1).用文件IO以只写的方式打开文件,文件不存在则创建,文件存在则清空 

(2).观察新建文件的权限

 

2.读写操作 

ssize_t read(int fd, void *buf, size_t count);

功能:从文件中尝试读取count个字节到buf中 

参数:

@fd     文件 

@buf    存放数据的地址 

@count  期望读取的字节数 

返回值:

成功返回实际读取的字节数,失败返回-1,如果读到文件尾部则返回0

参考man page如下:


-----------------------------------------------------------

ssize_t write(int fd, void *buf, size_t count);

功能:将buf中count个字节写入文件 

参数:

@fd     文件 

@buf    存放数据的地址 

@count  写入的字节个数

返回值:

成功返回实际写入的字节数,失败返回-1 

参考man page如下:


具体示例如下:



代码分析如下:

n = write(fd,write_buf,strlen(write_buf));

这条语句的作用是讲buf【】这个数组中的内容写到fd文件标示符(注意,这是系统调用函数,不是标准的I/O,所以不是流)中去,之后遇到这个语句:fd = open(argv[1],O_RDONLY);本已经打开了一次,且是同一个文件,为什么又要把同一个文件再打开一次呢?原因是这样的:因为之前写过一次之后,offset的值已经偏移了,所以若想再将文件中的内容从头开始读走,亦即让offset的值置为0,那么如何才能达到此效果呢?其中一个办法是把文件重新打开,所以也就出现了上述fd那条语句。之后又出现了 n = read(fd,read_buf,sizeof(read_buf));这条语句。其作用是将fd中的内容读到read_buf【】这个数组中,之后出现read_buf[n] = '\0';的原因是因为你若想把它当做字符串输出的话,则需要将其先变成字符串(因为文件中的内容不一定是字符串),亦即在其后加上‘\0’,这样就能达到字符串输出的效果。在这里我有一个疑问就是read_buf[n]中,它为什么不用sizeof(),因为虽然你是读sizeof()个字节,但是,它不一定就会读sizeof()个字节,这只是它的一个期望值而已。若想要知道read()函数成功读取的个数,只要看它的返回值即可。在read_buf[n]中,使用n的原因是数组下标是从0开始的,所以不能是n+1。再一个需要注意的是:这个是文件I/O,不是标准I/O,因为不带缓存,所以不需要刷新。

 

练习:通过文件I/O实现文件拷贝

 

3.文件定位 

off_t lseek(int fd, off_t offset, int whence);

功能:实现文件定位 

参数:

@fd        文件 

@offset    > 0 向后偏移 , < 0 向前偏移 

@whence    SEEK_SET,SEEK_CUR,SEEK_END 

返回值:

成功返回新的文件偏移值,失败返回-1

练习:

struct student 

{

char name[15];

int age;

int id;

};

struct student stu = {"xiaoming",10,100};

struct student stu1;

将stu写入文件,然后从文件中读取内容存放在stu1,输出stu1 

参考man page如下:



代码如下:



注:在读写文件时,需要注意文件中offset的值的变化,若读写同一个文件,记得将其offset的值置为你想要操作的位置。

 

三 文件属性的获得 

int stat(const char *path, struct stat *buf);

功能:获取文件属性 

参数:

@path  文件所在路径 

@buf   存放文件的属性 

返回值:

成功返回0,失败返回-1 

 

 

 

查看man page 如下:






-----------------------------------------------------------

具体示例如下:





getpwuid()函数,查看man page如下:



getgrgid()函数,查看man page 如下:



localtime()函数,查看man page如下:






 

 

 

代码分析如下:

I. 为什么不直接使用stat()中的成员,而是调用上述函数?因为若直接使用stat()中的成员的话,则它对应输出的是数字,而达不到我们想要的像ls那样格式输出的效果。所以就调用了上述的那些函数。

II. 对于第二种方法移位的解释:

对于第一种方法,我们好理解。对于第二种方法,其解释如下:对于文件的属性,我们从man page中可以知道,是通过st_mode的16个位来标识文件的属性的,所以,通过分析我们知道,最外层的那个循环是将每个大的文件所有者/组/其他组 依次移到最低的前三位,对于里面。那层循环,我们举个例子来理解吧!

for(I=0;i<3;i++)

data&(1<<i);

从上述那条语句中,我们分析可以知道,data的可能值可能有0,1,2,4所以通过内层那个循环,我们可以得到的目的是:是将对应的某一个组的

rwx分别通过data&(1<<i)这种方法移出来。

III.从st_mode中,我们可以知道,它是用16位来标识其文件属性的。通过得到的文件的st_mode & 对应的属性的宏,即可得到这个文件对应的单个的属性。

 

 

 

四 晚间作业 

./a.out  目录名 

->显示目录下每个文件的属性信息 

./a.out  普通文件名 

->显示普通文件的属性信息 

---------------------------------------------------

1.怎么判断用户传递的文件名是普通文件还是目录文件?

 

获取文件的属性信息,然后判断文件类型

2.如何对目录进行操作呢?

<1>打开目录文件 

DIR *opendir(const char *name);

功能:打开一个指定的目录文件 

参数:

@name 目录名 

返回值:

成功返回目录流,失败返回NULL 

<2>读一个目录文件  

void disply_dir(const char *dirname)

--------------------------------------------

struct dirent *readdir(DIR *dirp);

struct dirent *pdirent;

while( (pdirent = readdir(pdir)) != NULL)

{

printf("%s\n",pdirent->d_name);

}

测试:

./a.out file1 

./a.out . 

./a.out /home/linux 

代码示例如下:






 

 

关注微信公众号获取更多资讯


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值