《Unix/Linux系统编程》学习笔记2

第九章 I/O库函数

一.知识点归纳

(一)I/O库函数与系统调用

  • 系统调用函数:open()、read()、write()、lseek()、close()

  • I/O库函数:fopen()、fread()、fwrite()、fseek()、fclose()

image

二者区别:

  1. 在系统调用程序中,文件描述符fd是一个整数。在库I/O程序中,fp是一个文件流指针。
  2. 系统调用open()打开一个文件进行读取,并返回一个整数文件描述符fd,如果open()失败,则返回-1。I/O库函数fopen()返回一个FILE结构体指针,如果fopen()失败,则返回NULL。
  3. 系统调用程序使用while循环读取/写入文件内容。在每个迭代中,它发出read()系统调用,将最多4KB的字符读入buf[]。然后,它将各字符从buf[]写到文件描述符1中,这是该进程的标准输出。正如前文所指出的,使用系统调用一次写入一个字节非常低效。相反,I/O库程序仅仅使用fgetc(fp)从文件流中获取字符,通过putchar()输出字符,直至文件结束符。

(二)I/O库函数的算法

1.fread算法

使用 fread() 对文件进行读操作,函数原型如下:

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数和返回值含义:

参数 含义
ptr fread()将读取到的数据存放在参数 ptr 指向的缓冲区中
size fread()从文件读取 nmemb 个数据项,每一个数据项的大小为 size 个字节,所以总共读取的数据大小为 nmemb * size 个字节
nmemb 参数 nmemb 指定了读取数据项的个数
stream FILE 指针
返回值 调用成功时返回读取到的数据项的数目(数据项数目并不等于实际读取的字节数,除非参数size 等于 1);如果发生错误或到达文件末尾,则 fread()返回的值将小于参数 nmemb,那么到底发生了错误还是到达了文件末尾,fread()不能区分文件结尾和错误,究竟是哪一种情况,此时可以使用 ferror()或 feof()函数来判断

2.fwrite算法

fwrite() 库函数进行写操作,函数原型如下:

#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

参数和返回值含义:

参数 含义
ptr 将参数 ptr 指向的缓冲区中的数据写入到文件中
size 参数 size 指定了每个数据项的字节大小,与 fread()函数的 size 参数意义相同
nmemb 参数 nmemb 指定了写入的数据项个数,与 fread()函数的 nmemb 参数意义相同
stream FILE 指针
返回值 调用成功时返回读取到的数据项的数目(数据项数目并不等于实际读取的字节数,除非参数size 等于 1);如果发生错误,则 fwrite()返回的值将小于参数 nmemb(或者等于 0)

3.fclose算法

打开的文件通过 fclose() 进行关闭,函数原型如下:

#include <stdio.h>
int fclose(FILE *stream);

4.fopen算法

使用fopen() 打开或创建文件,函数原型如下:

#include <stdio.h>
FILE *fopen(const char *path, const char *mode);

参数和返回值含义:

参数 含义
path 参数 path 指向文件路径,可以是绝对路径、也可以是相对路径
mode 参数 mode 指定了对该文件的读写权限,是一个字符串,稍后介绍
返回值 调用成功返回一个指向 FILE 类型对象的指针(FILE * ),该指针与打开或创建的文件相关联,后续的标准 I/O 操作将围绕 FILE 指针进行。如果失败则返回 NULL,并设置 errno 以指示错误原因

参数 mode 补充:

mode 说明 对应于 open() 系统调用函数的 flags 参数取值
r 以只读方式打开文件 O_RDONLY
r+ 以可读可写方式打开文件 O_RDWR
w 以只写方式打开文件,如果参数path指定的文件存在,将文件长度截断为0,如果指定文件不存在 则创建该文件 O_WRONLY
w+ 以可读可写方式打开文件,如果参数path指定的文件存在,将文件长度截断为0;如果指定文件 不存在则创建该文件 O_RDWR
a 以只写方式打开文件,打开以进行追加内容(在文件末尾写入),如果文件不存在则创建该文件 O_WRONLY O_CREAT
a+ 以可读可写方式打开文件,以追加方式写入 (在文件末尾写入),如果文件不存在则创建该文件 O_RDWR

5.fseek算法和ftell算法

  • 库函数 fseek() 用于设置文件读写位置偏移量,和 lseek() 功能相同,但 lseek() 用于文件 I/O,而库函数 fseek() 则用于标准 I/O,函数原型:
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);

参数和返回值含义:

参数 含义
stream FILE 流指针
offset 与 lseek() 函数的 offset 参数意义相同,相对偏移位置的偏移值
whence 与 lseek()函数的 whence 参数意义相同,偏移位置
返回值 成功返回 0;发生错误将返回-1,并且会设置 errno 以指示错误原因;与 lseek()函数的返回值意义不同,这里要注意
  • 库函数 ftell() 可用于获取文件当前的读写位置偏移量,函数原型如下:
#include <stdio.h>
long ftell(FILE *stream);

参数 stream 指向对应的文件,函数调用成功将返回当前读写位置偏移量;调用失败将返回-1,并会设置 errno 以指示错误原因

(三)字符模式I/O

int fgetc(FILE *fp);		// get a char from fp, cast to int.
int ungetc(int c,FILE *fp);	// push a previously char got by fgetc()	back to stream
int fputc(int c,FILE *fp);	// put a char to fp

image
image

(四)行模式I/O

char *fgets(char *buf,int size,FILE *fp): 从fp中读取最多一行(以\n结尾)的字符。
int fputs(char *buf,FILE *fp): 将buf中的一行写入fp中。

image

image

(五)格式化I/O

  • 格式化输入

scanf(char *FMT,&item);		// from stdin
fscanf(fp,char *FMT,&items);	// from file stream
  • 格式化输出

printf(char *FMT,item);		// to stdout
fprintf(fp,char *FMT,items);	// to file stream

(六)内存中的转换函数

sscanf(buf,FMT,&items);		// input from buf[] in memory
sprintf(buf,FMT,items);		// print to buf[] in memory

二.问题与解决思路

1.argc、argv的具体含义是什么?

argc和argv参数在用命令行编译程序时有用。main( int argc, char* argv[], char** env ) 中:

  1. 第一个参数,int型的argc,为整型,用来统计程序运行时发送给main函数的命令行参数的个数,在VS中默认值为1。
  2. 第二个参数,char*型的argv[],为字符串数组,用来存放指向的字符串参数的指针数组,每一个元素指向一个参数。各成员含义如下:
    • argv[0]指向程序运行的全路径名
    • argv[1]指向在DOS命令行中执行程序名后的第一个字符串
    • argv[2]指向执行程序名后的第二个字符串
    • argv[3]指向执行程序名后的第三个字符串
    • argv[argc]为NULL
  3. 第三个参数,char** 型的env,为字符串数组。env[]的每一个元素都包含ENVVAR=value形式的字符串,其中ENVVAR为环境变量,value为其对应的值。平时使用到的比较少。

2.open()打开时flags传参O_RDONLY、O_WRONLY等的作用含义?

头文件:#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);

函数说明:

  • 参数 pathname 指向欲打开的文件路径字符串. 下列是参数flags 所能使用的旗标:
    • O_RDONLY 以只读方式打开文件
    • O_WRONLY 以只写方式打开文件
    • O_RDWR 以可读写方式打开文件. 上述三种旗标是互斥的, 也就是不可同时使用, 但可与下列的旗标利用OR(|)运算符组合.
    • O_CREAT 若欲打开的文件不存在则自动建立该文件.
    • O_EXCL 如果O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否则将导致打开文件错误. 此外, 若O_CREAT 与O_EXCL 同时设置, 并且欲打开的文件为符号连接, 则会打开文件失败.
    • O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.
    • O_TRUNC 若文件存在并且以可写的方式打开时, 此旗标会令文件长度清为0, 而原来存于该文件的资料也会消失.
    • O_APPEND 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面.
    • O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中.
    • O_NDELAY 同O_NONBLOCK.
    • O_SYNC 以同步的方式打开文件.
    • O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接, 则会令打开文件失败.
    • O_DIRECTORY 如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败。 注:此为Linux2. 2 以后特有的旗标, 以避免一些系统安全问题.
  • 参数mode 则有下列数种组合, 只有在建立新文件时才会生效, 此外真正建文件时的权限会受到umask 值所影响, 因此该文件权限应该为 (mode-umaks).

    • S_IRWXU00700 权限, 代表该文件所有者具有可读、可写及可执行的权限.
    • S_IRUSR 或S_IREAD, 00400 权限, 代表该文件所有者具有可读取的权限.
    • S_IWUSR 或S_IWRITE, 00200 权限, 代表该文件所有者具有可写入的权限.
    • S_IXUSR 或S_IEXEC, 00100 权限, 代表该文件所有者具有可执行的权限.
    • S_IRWXG 00070 权限, 代表该文件用户组具有可读、可写及可执行的权限.
    • S_IRGRP 00040 权限, 代表该文件用户组具有可读的权限.
    • S_IWGRP 00020 权限, 代表该文件用户组具有可写入的权限.
    • S_IXGRP 00010 权限, 代表该文件用户组具有可执行的权限.
    • S_IRWXO 00007 权限, 代表其他用户具有可读、可写及可执行的权限.
    • S_IROTH 00004 权限, 代表其他用户具有可读的权限
    • S_IWOTH 00002 权限, 代表其他用户具有可写入的权限.
    • S_IXOTH 00001 权限, 代表其他用户具有可执行的权限.

返回值:若所有欲核查的权限都通过了检查则返回0 值, 表示成功, 只要有一个权限被禁止则返回-1.

三.实践内容与截图

1.who1.c

image
image

2.复制文件

image

posted @ 2022-09-09 21:11  油菜园12号  阅读(65)  评论(0编辑  收藏  举报