linux系统编程-文件io与标准io

121 阅读12分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第31天,点击查看活动详情

若看到相同文章,为本人其他平台

文件IO

1、没有缓存区

2、操作对象不同,是文件描述符。

3、操作框架与标准IO相同,但函数名称没有f开头。

文件描述符

1、内核维护

2、非负整数

3、打开或者创建文件的返回值就是描述符

4、幻数 0 1 2

STDIN_FILENO STDOUT_FILENO STDERR_FILENO

5、所有文件描述符总是内核中的最小有效值

ctags:用来追源码用。

安装过程:

1、将压缩包拷贝到linux虚拟机中。

2、进入到文件所在的目录

3、解压文件包 tar -zxvf ctags-5.8.tar.gz

4、进入解开的ctags目录: cd ctags-5.8

5、在该目录下 ./configure ====>creating Makefile

6、在该目录下 make =====>没有 error即可

7、在该目录下 sudo make install

使用过程:

1、进入到 /usr/include/

2、sudo ctags -R *

3、vi -t STDIN_FILENO

在编程过程或者查看其他代码过程中可以对不认识的新变量或者宏

用该方式查找到最原始的定义。一般追查到最后出现基本数据类型为止。

4、:q 退出当前查找

5、 要往深入追查 用 ctrl+]

 要回退一次  用 ctrl+t


如果变量的类型是 typedef 定义的则要将光标放到 中间的 源变量上 按 ctrl+]
#define 定义的则要将光标放到 后面的 源变量上 按 ctrl+]

文件IO操作的基本流程: 打开 ====》读写 ====》关闭

头文件:

sys/types.h

sys/stat.h

fcntl.h

unistd.h ====>系统调用接口函数

打开:

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

功能:打开path指定的文件

参数:path 要打开的文件的路径+文件名称

flags O_RDONLY 只读方式打开

O_WRONLY 只写方式打开

O_RDWR 可读写方式打开

以上三种方式在打开文件时候一次只能选择一种方式。

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

功能:同上,唯一不同点在于该函数允许用户在文件不存在的时候创建文件,并赋文件权限。

参数: path 要打开的文件的路径+文件名称

flags 必须在三种基本方式选择一种以后,再增加一个O_CREAT 表示可以创建文件

同时mode参数有效

O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_APPEND

mode 文件权限,可以用8进制数据表示,0644 =》文件的权限对自己可读写,其他都是只读

注意:open 函数可以打开文件,但在单个进程中能打开的文件描述符上限值是1024 个。

返回值: 成功 返回一个最小未用的非负整数,就是文件描述符

失败 -1

文件iO的读写操作:

读操作: ssize_t read(int fd,void * buff,size_t count);

功能:从fd描述符所关联的文件中读count个字节的数据到buff所在的内存中。

参数: fd 要读数据的文件描述符

buff 要存储数据的内存区,一般是数组或者malloc的动态内存

count 要读的数据的长度,单位字节。

返回值:成功 返回读的数据的个数

失败 -1

注意:如果read的fd是标准输入设备则会出现阻塞等待的作用。

写操作: ssize_t write(int fd,const void * buff,size_t count);

功能:从buff的内存中取count个数据写入到fd关联的文件中。

参数:fd 要写入的文件描述符

buff 数据的来源,必须事先有数据存在。

count 要写入的数据长度,单位字节

返回值:成功 写入的数据长度

失败 -1

练习:

 1、通过write函数直接写 fd = 1 的标准输出设备,看是否有缓存区。

 2、通过read函数从终端获取用户的任意输入,并将用户输入打印到终端。要求用read 和 write函数实现。

 3、通过read和write函数实现文件IO的拷贝工具。

要求,可以主函数传参或者获取用户输入的源和目的文件名称 eg: mycopy src dst ====>src 的内容拷贝到dst

文件定位函数:lseek

原型: off_t lseek(int fd,off_t offset,int whence);

功能: 可以在文件中按照指定的offset和whence定位

参数: fd 要定位的文件描述符

offset 要偏移的位移量 >0 表示向文件后偏移

<0 表示向文件头偏移

whence 偏移的起始位置

SEEK_SET 从头偏移

SEEK_CUR 从当前位置偏移

SEEK_END 从文件末尾偏移

返回值:成功 当前位移量

失败 -1

注意:lseek只对常规文件有效,对设备文件,管道,socket fifo 无效

 lseek 只是偏移指针,没有IO 操作。

int close(int fd);

功能: 关闭已经打开的文件描述符

参数: fd 要关闭的文件描述符

返回值: 成功 0

失败 -1

///

文件属性的获取

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

功能:通过该函数可以将指定的path的文件属性信息存储到buff对应的结构体变量中。

参数: path 要获取属性的文件路径+名称

buff要存储属性信息的结构体指针

返回值:成功 0

失败 -1

注意 struct stat 结构中的成员变量重点

st_mode ====>文件的类型和文件的属性信息

st_size ====>文件的大小

文件类型 1、普通文件 -

2、目录文件 d

3、字符设备 c /dev/zero

4、块设备 b /dev/sr0

5、管道文件 f

6、套接字文件 s /dev/log

7、链接文件 l /dev/stdin

通过uid和给gid获取用户的用户名称和组名称

1、uid ===》name

头文件:pwd.h

结构: struct passwd ====>pw_name ===>name

函数: getpwuid();

流程:先定义一个结构体指针

   调用 getpwuid()函数将uid送入并获取返回值
   

输出返回值结果中结构体的成员变量name

2、gid ===>name

头文件: grp.h

结构: struct group ====>gr_name ===>name

函数: getgrgid()

流程:先定义一个结构体指针

   调用 getgrgid()函数将gid送入并获取返回值
   

输出返回值结果中结构体的成员变量name

目录操作 : 打开目录 ====》遍历目录 ====》关闭目录

头文件: dirent.h

结构体: struct dirent;====>目录的内容

struct DIR ; ====》目录的指针

打开目录:

DIR * opendir(const char * name);

功能:打开指定的name的目录。

参数:name 目录所在的路径+目录名称

返回值:成功 目录指针对象

失败 NULL;

遍历目录:

struct dirent * readdir(DIR * dirp);

功能:从指定的drip目录指针中获取一次当前目录中的文件信息。

参数: dirp要读的目录指针

返回值: struct dirent ===>d_name;

成功 d_name 就是获取到的文件名称

失败或到达目录最后一条记录: NULL

关闭目录:

int closedir(DIR * drip);

功能: 关闭之前打开的目录

参数:dirp 要关闭的目录

返回值: 成功 0

失败 -1

================================================

标准 io

IO:

标准的I设备: 键盘 ====》硬件键盘

标准的O设备: 显示器 ====》Linux 中的终端

一切皆为文件 ===》普通文件 目录文件 字符设备文件 块设备文件 管道 套接字 符号连接

文件流指针

缓存机制

作用: 提高效率 提高安全性

库文件: stdio.h

分类: 行缓存 ======>printf() 函数默认执行的是行缓存

1、stdio.h ===>函数默认都是带缓存的

2、操作对象 ====》标准输出 ===》屏幕显示器 ===》默认按照行缓存处理

3、所有的行缓存输出都是以 '\n' 刷新显示。

  满缓存 =====》当默认的缓冲区存储满了的时候,同步信息。

同步1: 将缓存区的数据同步到设备上,存储,显示。

同步2: 将缓存区的数据要清空,下次可以继续写入。

   标准输出设备的满缓存是: 1024 字节
   
   其他普通文件的满缓存是: 4096 字节




  不缓存=====》没有缓存区,直接同步 ====>一般在stderr流中使用。
  

原因:所有的错误信息应该及时同步,不应该累计。

注意:目前在标准IO中只有标准错误输出是不缓存的。

文件流: 类型 1 文本流

 2  二进制流

文件流指针: 文件流的说明性质的标示====》FILE * fp;

printf() == fprintf(stream,...);====>stream == stdout;

printf(“hello\n”); == fprintf(stdout,"hello\n");

系统默认的标准流对象:

1、stdin =====>标准输入流 == 键盘 ====》行缓存

2、stdout =====》标准输出流 == 显示器终端 =====》行缓存

3、stderr =====》标准错误输出流 ====》显示器终端 ====》不带缓存

以上流对象在应用程序中,默认是自动打开的。

所有的程序满足如下条件之一则同步数据:

1、满缓存

2、强制刷新 =====》fflush(FILE * stream); 3、程序结束

/dev/null =====>黑洞 垃圾桶

a.out 1>/dev/null ===》将a.out执行过程中本来要输出到stdout 的数据都发送到 /dev/null

结果将不显示 stdout的输出信息。

操作函数

基本框架: 打开文件 =====》读写文件 (定位文件)====》关闭文件

打开: fopen();

原型: FILE * fopen(const char * path,const char * mode);

功能说明:根据指定的路径和方式打开一个文件并并返回文件流指针对象 。

参数: path : 文件所在的路径+ 文件名称 ===》字符串表示

mode: 文件打开的方式;=====》字符串方式 "r","w+"

r ===>只读 文件必须存在

r+ ===》可读写, 文件必须存在

w ====》只写, 文件不在则创建 存在则清空

w+ ===》可读写,文件不在则创建,存在则清空

a ===>只写 ,文件不在则创建, 存在则追加

a+ ===》可读写,文件不在则创建,存在则追加

注意:以上mode方式在打开文件时候一次只能选择一种方式。

返回值:成功返回一个非NULL的文件流指针对象

失败 NULL ,并设置errno

基本的写操作: fprintf(stdout,"hello \n");

fprintf(fp,"hello \n");

freopen(const char * path, const char * mode ,FILE * steam);

功能:将已经打开的文件流指针重新打开并关联到指定的文件中。

参数: path 要新打开的文件路径+名称

  mode 要新打开的文件方式===》r r+  w w+ a a+
  
  stream  已经打开的文件流对象
  

返回值:成功 转移后的文件流对象

失败 NULL;

perror();

原型: void perror(const char *s);

功能: 打印系统错误。根据当前系统获取到的errno值打印其对应的错误信息。

参数: s 要打印的信息,实际输出过程中会将该信息加到系统错误信息的前面。

返回值: 无

读写: 三组:

1、 单字符读写: fgetc() fputc()

I: fgetc();===>读函数

getchar() <==变形=getc(stdin)<==宏= fgetc(stdin);

原型: int fgetc(FILE * stream);

功能:从已经打开的流对象中获取一个字符

参数: stream 已经打开的流对象

返回值: 成功 获取到的字符

失败 -1(EOF);

o: fputc();===》写函数

putchar() < ==变形===putc()<===宏=== fputc(c,stdout);

原型: int fputc(int c,FILE * stream);

功能: 将指定的字符c写到stream对象中。

参数: c 要写入的字符

stream 要写入的流对象

返回值:成功 写入的字符

失败 -1 (EOF)

默认流对象: stdin stdout stderr

fgetc();====> c = fgetc(stdin);

fputc();====> ret = fputc(c,stdout);

练习:用最少的代码以fgetc fputc实现从终端打印输出用户的任意输入。

判断文件结尾的方式:

1、fgetc() 的返回值如果是 -1 (EOF) 代表文件访问异常或者文件到达结尾。

{每次进行一次fgetc()操作 指针就会向后偏移一次}

2、feof() 函数可以检测当前文件是否到达结尾,如果到达结尾则该函数返回值是真,否则为假

该函数再使用过程中一定要先做一次IO操作之后再判断。

while(1)

{

c = fgetc(fp);

if(feof(fp)) break;

}

if(c == EOF) break;

练习:用主函数传参的方式指定文件并将文件的内容打印到终端,要求必须用fgetc 和fputc 函数实现。

eg:./a.out abc ===>把abc文件的内容打印到终端。

如果文件不存在则提示用户文件不存在。

思路: 主函数传参数 ===》argv[1] 获取

以只读方式打开文件 ===》 fopen(argv[1],"r");

用循环方式读取文件内容 ===》 c = fgetc(fp);

如果到达文件结尾则循环结束 ===》if(EOF == c) break;

如果没有到达文件结尾,则显示数据 ===》fputc(c,stdout);

程序结束之后关闭文件退出。

2、 按行读写: fgets() fputs()

char * fgets(char *buff,int size,FILE * stream);

功能: 通过该函数可以将制定的流对象中指定大小的数据取到buff中。

参数: buff要存储数据的本地内存,一般是数组或者malloc的内存区

size 要获取的数据长度,单位字节,长度一般是内存大小减1 而且不能有换行符号

stream 要获取的流对象

返回值:成功 获取到的数据内存区首地址

失败 NULL;

int fputs(const char * buff,FILE * stream);

功能: 将指定的buff中的数据写入到stream对象中。

参数: buff要写的信息,一般是已经存储好的字符串stream要写的流对象,一般已经打开。

返回值: 成功 0

失败 -1

3、 二进制读写: fread () fwrite()

原型: size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream);

功能: 从指定的stream对象中获取指定nmemb个数的单位为size的数据到ptr所指向的内存区。

参数: ptr 要存储数据的内存区,一般是具体的结构体或者实体的类型。

size 要获取的数据单位的大小,一般是 sizeof(struct sadfa);

nmemb 要获取的数据的个数,一般是实际的数据块个数

stream 要获取的数据来源对象。

返回值: 成功 获取到的数据的个数,一般小于或者等于nmemb;

失败 -1;

如果要判断文件结尾,可以用返回值 == 0 来判断。

原型: size_t fwrite(const void * ptr,size_t size,size_t nmemb,FILE * stream);

功能: 该函数可以从ptr的内存中获取nmemb个大小是size的数据写入到stream 流对象中。

参数: ptr 数据源所在的内存区,一般是有数据的数据块或者结构体。

size 要写入的数据单位大小

nmemb要写入的数据的个数

stream要写入的流对象

返回值: 成功 写入的数据个数,小于或者等于nmemb

失败 -1

关闭: fclose();

原型: int flose(FILE * stream);

功能: 关闭一个已经打开的文件流对象

参数: stream 已经打开的流对象

返回值: 成功 0

失败 -1 ;并设置 errno

注意:以上的IO操作函数中,如果要判断文件结尾一般用

fgetc 的返回值 ==== EOF 到文件结尾

fgets 的返回值 ==== NULL 到文件结尾

fread 的返回值 ==== 0 到文件结尾

不论以上那种都可以支持用feof()函数来检测。

//=================man手册的使用============================

linux 中应用层函数的学习要依赖man手册。

man 函数名 ===》查询函数名称默认的说明,如果直接man 其后跟的函数有对应的命令,则先显示命令的内容。

man 函数名 == man 1 函数名;====》函数名可以是命令

man 2 函数名 ===》查询系统调用函数相关说明===>unistd.h

man 3 函数名 ===》查询系统库函数相关说明 ===》stdio.h

//=================man手册的使用============================

文件定位函数

int fseek(FILE *stream ,long offset,int whence);

功能:通过该函数可以在文件流对象中定位

参数: stream 要定位的流对象

offset 要定位的偏移量 如果值是大于0的则向文件内容后偏移

小于0的则向文件开头部分偏移

whence 要定位的起始位置

SEEK_SET 表示起始位置是文件开头

SEEK_CUR 表示起始位置是文件当前位置

SEEK_END 表示起始位置是文件末尾

返回值:成功 0

失败 -1

long ftell(FILE * stream) ===>从流中获取当前文件指针所在的从头文件开头到目前的位置,单位是字节。

void rewind(FILE * stream); ====>等价于 fseek(stream,0L,SEEK_SET)

表示将文件流指针偏移到文件开头部分。


注意:文件定位函数操作过程中不能操作设备文件fseek定位函数在文件打开过程中不能是 a 或者 a+;

查看IP地址命令: linux ifconfig

window ipconfig