-
前言
- 当前笔记是基于我现在使用的centos6.3系统,各种细节最终以当前使用系统的man手册为准。
- 文本IO的函数位于man手册第二部分,man 2 命令名 打开文本IO帮助文档。
-
stdio(标准IO)与文本IO(系统调用IO)的区别
- 文本IO:无缓冲,响应速度快。
- 标准IO:有缓冲,吞吐量大。
- 尽可能使用标准IO。可移植性好,吞吐量大,提高系统整体效率。
- 不要标准IO与文本IO混用。标准IO存在缓冲,两者混用,基本会出问题。
- 例子如下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
putchar('a');
write(1,"b",1);//注意这里不能写成write(stdout,"b",1)。stdout是FILE指针类型;
putchar('a');
write(1,"b",1);
exit(0);
}
输出结果为:aabb。
原因:putchar属于标准IO,它先存入缓冲区。
-
fileno
int fileno(FILE *stream);
返回stream对应的文件描述符。 -
fdopen
FILE *fdopen(int fd, const char *mode);
以mode模式打开fd,返回文件流。更多细节阅读man 3 fdopen。 -
open
-
功能描述:
打开一个文件或者设备。
-
函数定义:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);- open使用可变参数实现。
- pathname:文件名
- flags:(以下三种必选其一)
- O_RDONLY:只读
- O_WRONLY:只写
- O_RDWR:读写
- 文件创建标志与文件状态标志可与以上三种进行或运算,一起放在flag位上。
- 文件创建标志:
- O_CREAT:文件不存在,则创建文件
- O_EXCL:
- O_NOCTTY
- O_TRUNC.:文件存在,截断文件,长度为0。文件不存在,则创建文件,长度为0.
- 文件状态标志:(待补充)
- mode(只有使用了O_CREAT时,mode才有意义。)
- mode=00700,则代表创建的文件,user 用户的权限为rwx。
- mode=00070,则代表创建的文件,group用户的权限为rwx。
- 同理,可以得到更多的三类用户的权限组合。
- 经过 (mode & ~umask)计算后,才得到创建的文件最终的权限。
-
返回值
执行成功时,返回文件描述符。否则返回-1.并设置errno值。
-
-
read
-
write
-
lseek
-
功能描述
重定向读写文件指针偏移量。
-
函数定义
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);- whence:
- SEEK_SET:文件首部
- SEEK_CUR:文件指针当前位置
- SEEK_CUR:文件尾。
- offset:偏移量
- whence:
-
lseek允许offset+whence超过文件长度,这不会改变文件长度。如果在这个位置还写了数据,那在这个位置读数据时,会返回’\0’,直到文件长度扩展后,包括了之前的位置。
-
返回值
- 成功的话,返回现在的指针位置与文件头之间的偏移量。
- 失败的话,返回-1,并设置errno值。
-
-
close
-
使用open、read、write、close实现cp
/****************
这个程序并没有考虑中断等各种异常情况,等待之后学习,再完善。
********************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFFSIZE 1024
int main(int agrc,char ** argv)
{
int sfd,dfd;
int len;
char buff[BUFFSIZE];
if(agrc !=3)
{
printf("Usage----%s <source file><dest file>",argv[0]);
exit(EXIT_FAILURE);
}
sfd=open(argv[1],O_RDONLY);
if(sfd<0)
{
perror("sfd open()");
exit(EXIT_FAILURE);
}
dfd = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0600);
if(dfd<0)
{
perror("dfd open()");
close(sfd);
exit(EXIT_FAILURE);
}
while(1)
{
len = read(sfd,buff,BUFFSIZE);
if(len ==-1)
{
perror("read()");
break;
}
if(len==0)
break;
write(dfd,buff,len);//可能一次没写入全部len个数据。等待之后完善。
}
close(sfd);
close(dfd);
exit(EXIT_FAILURE);
}
-
truncate、truncate
-
函数定义
#include <unistd.h>
#include <sys/types.h>int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length); -
函数功能:
截取文件的前length个字节,若文件长度不到length,则多余的部分填充’\0’。若长度超过length,则多余的部分,数据丢失。文件偏移不改变。
-
注意事项
- truncate path是文件地址,ftruncate的fd是已经打开的文件描述符。
- truncate要求该文件可写;ftruncate要求该文件以可读的方式打开。
- 执行成功,返回0;执行失败返回-1,且设置对应的errno。
-
-
使用read、write、ftruncate等实现删除某文件的第10行
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFFSIZE 1024
//初始化两个文件描述符
int init(int * _rfd,int * _wfd)
{
*_rfd = open("text",O_RDONLY);
if(*_rfd == -1)
{
perror("rfd open()");
return -1;
}
*_wfd=open("text",O_RDWR);
if(*_wfd == -1)
{
perror("wfd open()");
close(*_rfd);
return -1;
}
return 0;
}
int findLine(int * _line10Position,int * _line11Position,int _rfd)
{
int len=0, //记录每次read读取的字节数
count= 0, //总共已经判别是否为'\n'的字节数
tmp=0, //记录每次read后,判别了多少个字符是否为'\n'
flag=1, //0--标志找到了第9和第10个‘\n’,也就是存在第十行
countLine=1;//已经找到了几个'\n'
char buff[BUFFSIZE];
while(flag)
{
tmp=0;
len = read(_rfd,buff,BUFFSIZE);
if(len==-1)
{
perror("read");
break;
}
if(len==0)
break;
while(tmp<len)//寻找第9和第10个\n
{
count++;
if(buff[tmp]=='\n')
{
countLine++;
if(countLine==10)
{
*_line10Position=count;
}
if(countLine==11)
{
*_line11Position=count;
flag=0;
break;
}
}
tmp++;
}
}
return flag;
}
int truncate10Line(int _rfd,int _wfd,int _line10Position,int _line11Position)
{
int len;
char buff[BUFFSIZE];
// fprintf(stdout,"找到10行\n");
// printf("line10=%d,line11=%d\n",_line10Position,_line11Position);
// ftruncate(_wfd,_line10Position);
lseek(_wfd,_line10Position,SEEK_SET);
lseek(_rfd,_line11Position,SEEK_SET);
// fprintf(stdout,"进入覆盖循环");
while(1)
{
len=read(_rfd,buff,BUFFSIZE);
fprintf(stdout,"len=%d\n",len);
if(len == -1)
{
perror("read()");
return -1;
}
if(len==0)
break;
_line10Position+=len;
// fprintf(stdout,"读取%d\n",len);
write(_wfd,buff,len);
}
ftruncate(_wfd,_line10Position);
return 1;
}
int main()
{
int rfd,wfd;
int line10Position,line11Position;
if(init(&rfd,&wfd)==-1)//初始化两个文件描述符失败
exit(EXIT_FAILURE);
if(!findLine(&line10Position,&line11Position,rfd))//存在第十行
{
if(truncate10Line(rfd,wfd,line10Position,line11Position) == -1)//删除第十行失败
{
close(rfd);
close(wfd);
exit(EXIT_FAILURE);
}
}
else
fprintf(stdout,"不存在第十行!\n");
close(rfd);
close(wfd);
exit(EXIT_SUCCESS);
}
-
dup、dup2重定向与原子操作
-
dup
-
函数定义
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd); -
函数功能
- 建一个oldfd的副本。占用另一个文件描述符。
- dup使用现在没在使用的最小的文件描述符。
- dup2,这是原子操作,立马在newfd创建一个oldfd的副本。
- 若oldfd不是一个有效的文件描述符,则执行失败,newfd也不会被关闭。
- 如oldfd是一个有效的文件描述符,但newfd和oldfd的值相同,则dup2不作任何改变,返回newfd。
- dup、dup2执行成功的话,新旧两个文件描述符可以互换使用。它们关联同一个文件描述符,同时共享文件偏移和文件状态标志(file status flag)。比如 ,其中一个使用lseek修改了文件偏移,另一个的文件偏移也会跟着修改。
- 但是两者不会共享文件描述标志(file descriptor flags),The close-on-exec flag (FD_CLOEXEC; see fcntl(2)) for the duplicate descriptor
is off.
-
例子
- 程序一程序功能:关闭默认的stdout流,再打开一个流,利用dup使puts重定向到文件中去。
- 可能情况一:默认stdout没打开,close(1)可能出错。
- 可能情况二:stdout打开的,而且也是1。在close(1)后,刚好有其他程序占用了文件描述符1,则这个函数打开的fd!=1。于是puts就输出到其他程序中去了。
- 出问题的原因在于dup不是原子操作,且没考虑stdout打没打开的问题。
-
鉴于以上,使用dup2实现,也就是下面的程序二。这是原子操作。
-
由于我们这个程序把文件描述符1修改了。在大型项目中,会影响到别人的程序,因此还需要恢复现场。
-
//程序一
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/tmp/out/text"
int main()
{
int fd;
close(1);
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);//关闭1后,新打开的会选择现在最小的文件描述符打开。
if(fd==-1)
{
perror("open()");
exit(EXIT_FAILURE);
}
puts("hello!");
exit(EXIT_SUCCESS);
}
//程序二
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/tmp/out/text"
int main()
{
int fd;
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);//关闭1后,新打开的会选择现在最小的文件描述符打开。
if(fd==-1)
{
perror("open()");
exit(EXIT_FAILURE);
}
if(dup2(fd,1) == -1)
{
perror("dup2()");
exit(EXIT_FAILURE);
}
if(fd!=1)//dup2,oldfd与newfd相同时,返回newfd,且不做任何修改。
close(fd);
puts("hello!");
exit(EXIT_SUCCESS);
}