APUE笔记-第三章文件I/O(系统I/O)

其他章节

第四章 文件和目录
第五章 标准IO
第六章 系统数据文件和信息
第七章 进程环境
第八章
第九章
第十章


提示:个人学习笔记,无参考价值


第三章 文件I/O (系统I/O)

1. 文件描述符

类型是整形,是数组的下标,并且文件描述符优先使用数组当前可用范围内最小的下标
在这里插入图片描述



2. 文件I/O操作相关函数

open() 与 close()

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
//成功返回文件描述符,失败返回-1并设置errno
int open(const char *pathname, int flags, mode_t mode);

#include <unistd.h>
int close(int fd);

参数 flags 必须包含其中一项: O_RDONLY, O_WRONLY, or O_RDWR。表示只读,只写和可读可写。

另外,多个文件的创造模式和状态模式一起并起来赋给flags。文件创造模式有O_CLOEXEC,O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_TMPFILE和O_TRUNC。文件状态模式有 O_APPEND, O_ASYNC和O_CREAT等等。

例如: r  -> O_RDONLY
	  r+ -> O_RDWR
	  w  -> O_WRONLY | O_CREAT | O_TRUNC(只可写,无则创建,有则覆盖)
	  w+ -> O_RDWR | O_CREAT | O_TRUNC (可读可写,无则创建,有则覆盖)

【详解】
open函数一般用于打开或者创建文件,在打开或创建文件时可以制定文件的属性及用户的权限等各种参数。

第一个参数path表示:路径名或者文件名。路径名为绝对路径名(如C:/cpp/a.cpp),文件则是在当前工作目录下的。

第二个参数oflags表示:打开文件所采取的动作。可能值:必须指定下面某一种:
	O_RDONLY(只读),
	O_WRONLY(只写),
    O_RDWR(可读可写)
打开/创建文件时,至少得使用上述三个常量中的一个,以下常量是选用的:
	O_APPEND       每次写操作都写入文件的末尾
	O_CREAT        如果指定文件不存在,则创建这个文件
	O_EXCL         如果要创建的文件已存在,则返回 -1,并且修改errno的值
	O_TRUNC        如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
	O_NOCTTY       如果路径名指向终端设备,不要把这个设备用作控制终端。
	O_NONBLOCK     如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)

第三个参数mode表示:设置文件访问权限的初始值。(与用户掩码umask变量有关,实际的访问权限有mode &~umask确定)

   S_IRUSR, S_IWUSER, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH.
   其中S_I:前缀,R:读,W:写,X:执行,USR:文件所属的用户,GRP:文件所属的组,OTH:其他用户。
  【注】第三个参数是在第二个参数中有O_CREAT时才用作用。若没有,则第三个参数可以忽略。

返回值:如果操作成功,它将返回一个文件描述符,如果失败,返回-1

read() write() lseek()

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
//whence的取值:SEEK_SET SEEK_CUR 或 SEEK_END
  • read
    参数buf:存储读取的内容
    参数count:指定想要读取的字节个数
    返回值:成功返回真正读取到的字节数(0代表文件读到末尾);失败返回-1并设置errno

  • write
    参数buf:存储要写入的内容
    参数count:指定想要写入的字节个数
    返回值:成功返回真正写入的字节数;失败返回-1并设置errno

  • lseek
    成功返回文件开头到当前位置的偏移量,失败返回-1并设置errno。
    相当于fseek和ftell的结合

案例 复制文件

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define BUFSIZE 1024

int main(int argc, char *argv[]){
    if (argc < 3){
        fprintf(stderr,"usage: %s srcfile destfile\n",argv[0]);
        exit(1);
    }

    int fds,fdd;
    char buf[BUFSIZE];
    int len,ret,pos;

    fds = open(argv[1], O_RDWR);
    if (fds < 0){
        perror("open(srcfile)");
        exit(1);
    }

    fdd = open(argv[2], O_WRONLY|O_CREAT,O_TRUNC,777);
    if (fdd < 0){
        perror("open(destfile)");
        close(fds);
        exit(1);
    }

    while (true){
        len = read(fds,buf,BUFSIZE);
        if (len < 0){
            perror("read(fds)");
            break;
        }
        if (len == 0){
            break;
        }

        pos = 0;
        //ret有可能不等于len,即len个字节没有完全写完
        while (len > 0){
            ret = write(fdd,buf+pos, len);
            if (ret < 0){
                perror("write(fdd)");
                exit(1);
            }
            pos += ret;
            len -= ret;
        }

    }

    close(fdd);
    close(fds);

    exit(0);
}


3. 文件IO与标准IO的区别

  • 系统IO操作文件时是无缓冲输入,一般用于操作字符设备文件(LCD, LED, BEEP …,数据需要实时刷新)标准IO操作文件时是有缓冲输入,一般用于操作普通文件(.txt, .jpg…)
  • 系统IO是Linux操作系统提供函数接口,标准IO标准C语言库函数提供的。标准IO是对系统IO的包装,在标准IO的实现中,实际上调用系统IO提供的函数。
  • 系统IO响应速度快,而标准IO吞吐量大

【注意】文件IO与标准IO不可以混用

比较示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){

    putchar('1');
    write(1,"a",1);
    putchar('2');
    write(1,"b",1);
    putchar('3');
    write(1,"c",1);

    //输出结果:abc123
    exit(0);
}

因为 putchar是标准io函数,具有缓冲区,而write是系统io函数,没有缓存区,直接输出,所以会先输出abc。等运行到exit(0)时,会刷新缓存区,输出123。

4. 原子操作

不可分割的操作。作用:解决竞争和冲突

5. dup() 与 dup2()

先看一个案例,完成输出重定向,代码如下:

#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    close(1);

    int fp = open("./dup.txt",O_RDWR|O_CREAT|O_TRUNC|0600);
    if (fp < 0){
        perror("open()");
        exit(0);
    }

    puts("hello");	
    exit(0);
}

close(1);关闭了标准输出流,fp由open创建,是当前可用的最小整数,即1。 上述代码将puts()的输出,由标准输出流改成fp。即输出到文件./dop.txt中。

#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
  • dup函数创建一个新的文件描述符,该新文件描述符和原有文件描述符oldfd指向相同的文件、管道或者网络连接。并且dup返回的文件描述符总是取系统当前可用的最小整数值。

    #include<stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    int main(){
    	int fd = open("./dup.txt",O_RDWR|O_CREAT|O_TRUNC|0600);
    	if (fd < 0){
        	perror("open()");
       	 	exit(1);
    	}
    
    	close(1);
    	int newFd = dup(fd);    //复制fd文件描述符,
    	close(fd);
    	//close(newFd);
    
    	puts("hello");
    	exit(0);
    }
    

    //往dup.txt写入hello

  • dup2()函数,完成的功能与dup一样。当newfd被占用时,会先关闭该文件描述符,再将oldfd复制一份,放在newfd中。当oldfd与newfd相等,则函数直接返回newfd,没有关闭操作。

    #include<stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    int main(){
    	int fd = open("./dup.txt",O_RDWR|O_CREAT|O_TRUNC|0600);
    	if (fd < 0){
        	perror("open()");
        	exit(1);
    	}
    
    	//close(1);
    	//int newFd = dup(fd);    //复制fd文件描述符,
    
    	dup2(fd,1); //是原子操作,完成上面两句代码的功能
    
    	if (fd != 1)
    		close(fd);
    
    	puts("hello");
     	exit(0);
    }
    

6. fcntl() 与ioctl()

fcntl详解
ioctl详解

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );

fcntl()针对(文件)描述符提供控制。参数fd是被参数cmd操作(如下面的描述)的描述符。针对cmd的值,fcntl能够接受第三个参数int arg。

fcntl()的返回值与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列命令有特定返回值:F_DUPFD , F_GETFD , F_GETFL以及F_GETOWN。

F_DUPFD   返回新的文件描述符
F_GETFD   返回相应标志
F_GETFL , F_GETOWN   返回一个正的进程ID或负的进程组ID

fcntl函数有5种功能:

  1. 复制一个现有的描述符(cmd=F_DUPFD).
  2. 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
  3. 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
  4. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
  5. 获得/设置记录锁(cmd=F_GETLK , F_SETLK或F_SETLKW).

补充:str类函数族

strlen

功 能: 返回字符串长度。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char * str = "123";
    printf("strlen = %ld\n", strlen(str));  //3
    exit(0);
}

stpcpy

功 能: 拷贝一个字符串到另一个

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    char * str1 = "shahao";
    char str2[10];
    strcpy(str2,str1);
    printf("%s\n",str2);
    exit(0);
}

strcat

功 能: 字符串拼接函数

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(){
    char  str1[10] = "shahao";
    char * str2 = "kaikai";
    strcat(str1,str2);
    printf("%s\n",str1);
    exit(0);
}

strcmp

功 能: 看Asic码,str1>str2,返回值 > 0;两串相等,返回0

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(){
    char * str1 = "shaohao";
    char * str2 = "shihao";
    if (strcmp(str1,str2) > 0){
        printf("str1 = %s \n",str1);
    } else if (strcmp(str1,str2) < 0){
        printf("str2 = %s \n",str2);
    } else{
        printf("str1 == str2\n");
    }

    exit(0);
}
//输出结果:shihao

strchr

功能为查找str中首次出现c的位置,如有有,则返回出现位置,否则返回NULL。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    char * str = "shahaoshihao";
    char * c = strchr(str,'a');
    printf("%c\n",*c);  //a
    printf("%s\n",c);   //ahaoshihao
    exit(0);
}

strrchr

功能为查找str中最后一次出现c的位置,如有有,则返回出现位置,否则返回NULL。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    char * str = "shahaoshihao";
    char * c = strrchr(str,'a');
    printf("%c\n",*c);  //a
    printf("%s\n",c);   //ao
    exit(0);
}

strstr

功能为查找字符串str2在str1中出现的位置,找到则返回位置,否则返回NULL 。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    char * str = "shahaoshiha---sshihao123";
    char * c = "shihao";
    char * str1 = strstr(str,c);
    printf("%c\n",*str1); //s
    printf("%s\n",str1); //shihao123
    exit(0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值