Linux的标准IO文件操作

LINUX 中一切皆文件。
7 种类型的文件 :
1、 常规文件 ( -  日常用到最多 );
        1) ASCII码文件;
        2) 二进制的文件;
2、 目录 ( d 文件夹;目录下创建子目录 / 文件 );
3、 字符设备 (c 设备文件;代表一个具体设备 按键 i2c);
4、 块设备 (b 设备文件;磁盘 u );
5、 有名管道 (p 进程间通信 );
6、 套接口 (s 网络通信 );
7、 符号连接 ( 类似 windows  快捷方式 );

LINUX 中设备分为字符设备,块设备,网络设备三种;设备节点(设备加载了设备驱动后,在/dev 目录下就会有一个文件和对应)。

1.标准 IO

在标准 I/O 中,流用 FILE * 来描述。
标准 IO 有缓冲 ( 在系统提供的 C 库函数的缓存 ), 分为 全缓冲,行缓冲和无缓冲

最里面是 Unix 内核,在它之上是 Unix 的系统调用,各种库文件是建立再系统调用之上的,可以由用户程序(application )调用, Shell 是一个比较特殊的应用程序( application ),它提供了运行其他用户程序的接口。用户程序(application )可以在 Shell 上运行,可以调用 Library 运行,也可以直接调用 system calls 运行。
标准 IO 通过内部缓冲机制减少系统调用, 实现更高的效率。 标准IO 的所有操作都是围绕“流”来进行。流负责操作缓冲区, 输入输出是操作的实现,流操作是通过文件指针来操作。
流( stream
定义:所有的 I/O 操作仅是数据简单的从程序移进或者移出,这种字节流,就称为流。
分类:文本流 / 二进制流。
流的特点 :   有序连续 , 具有方向性。

标准IO/缓冲IO

1) 不带缓区
 
标准错误输出 stderr 流不缓冲,只要有数据就刷新;
例如在 malloc 是如果是 NULL perror() 标准错误输出
        p=malloc()
        if(NULL==p){
                perror("malloc");
        }
2) 行缓存
标准输入 stdin 键盘 / 输出 stdout 流屏幕;
例如 printf 什么时候显示到屏幕上 ?
行缓存:
(1). 遇到换行符
(2). 程序正常结束 ( 有些异常退出不刷新缓存 )
(3). 刷新 fflush() 强制
3) 全缓存 读写磁盘文件
全缓存呢?
(1) 当缓存区满
(2) 程序正常结束
(3) 刷新 fflush()
要求填满整个缓冲区后才进行 I/O 系统调用操作。对于磁盘文件的操作通常使用全缓冲的方式访问。第一次执行 I/O 操作时, ANSI 标准的文件管理函数通过调用 malloc 函数获得需要使用的缓冲区,默认大小为 8192

非缓冲IO/文件IO

系统不自动开辟确定大小的缓冲区,而是由程序为每个文件设定缓冲区。
修改缓冲区类型和大小可调用下面的函数。

标准 IO 文件操作

打开文件
在标准 IO 编程中,对文件操作之前必须先打开文件,打开文件用: fopen()
        FILE *fopen(const char *path, const char *mode);
        path:要打开的文件路径
        made:打开的方式
r     只读
r+   可读写 , 文件必须存在
w   只写
        若文件存在清空再写                
        若文件不存在创建新文件
w+ 读写
a     只写
        若文件存在末尾追加写
        若文件不存在创建新文件
a+   读写
 一次读一个字符
        
        int getchar(void) ;
        //默认从标准输入读数据
        
        int getc(FILE *stream);
        //从指定的文件中读数据 类似 fgetc 用宏来实现
        
        int fgetc(FILE *stream);
        //从指定的文件中读数据
        fgetc 从 fopen 返回的 stream 读一个字符 , 返回 unsigned char 并转换 int
        
        当读到文件末尾或出错返回 EOF
        函数 getchar() 等同于 getc(stdin)
        读一个
        ch=fgetc(fp);printf("%c",ch);
        我想多读几个怎么办?
        以上有局限性不知道要读多少
        主要要理解文件指针的移动
        用 getchar 从键盘输入字符并打印到屏幕上 , 直到输入 # 为止
        ch=getchar() !=’#’
        printf(“%c”,ch);
一次写一个字符
        int putchar(int c);
        //默认向标准输出写数据
        int putc(int c, FILE *stream);
        //向指定的文件中写数据
        int fputc(int c, FILE *stream);
        //向指定的文件中写数据
        函数 putchar(c) 等价于 putc(c,stdout)
        返回EOF 表示出错了 , 返回 unsigned int 表示写的字符
        
        以上是一次读写一个字符, 但是当文件很大一次读一个字符效率太低
#include <stdio.h>
#include <stdlib.h>

/*********************
 * 按字符copy文件
 * xurx
 * 2022/10/12
 **********************/

int main(int argc,char ** argv){

	char ch = 0; 

    if(argc != 3){
		printf("输入参数不正确\n");
		exit(1);
    }

	FILE * fb = fopen(argv[1], "r");
    if(fb == NULL){
		printf("打开文件失败\n");
		exit(1);		
	}

	FILE * fw = fopen(argv[2], "w");

	while((ch =fgetc(fb)) != EOF){
		fputc(ch, fw);
	} 

	fclose(fb);
	fclose(fw);

	printf("文件copy成功\n");

    return 0;
}
一次读写一行
        char *gets(char *s);
        //默认从键盘读数据,不读换行符 '\n'
        
        int puts(const char *s);
        //默认向屏幕输出数据 自带换行符
        char *fgets(char *s, int size, FILE *stream);
        //从 stream 指定的文件中读数据
        int fputs(const char *s, FILE *stream);
        //向 stream 指定的文件中写数据
        从 stream 流读最多 size 大小的 char 读到哪里 ?
        读到 s 里面去 , 当读到 EOF 或换行符 \n 结束;
        把读到新行保存到 s 中并且添加 '\0';
        返回值,读完或出错返回 NULL;
        一次读一行 stream-->size-->s;
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>


/*********************
 * 按行copy文件
 * xurx
 * 2022/10/12
 **********************/

#define CACHE_SIZE  256

int main(int argc,char ** argv){

    char ca[CACHE_SIZE] = {0};

    if(argc != 3){
		printf("输入参数不正确\n");
		exit(1);
    }

	FILE * fb = fopen(argv[1], "r");
    if(fb == NULL){
		printf("打开文件失败\n");
		exit(1);
	}

	FILE * fw = fopen(argv[2], "w");

    while(fgets(ca, CACHE_SIZE, fb) != NULL)
    {
		fputs((char *)ca,fw);  //返回非负的值表示成功
		
		bzero(ca, CACHE_SIZE);
	}

	printf("文件copy成功\n");
	
    return 0;
}

工程问题 : 防止数组的下标越界 确定数组最后一个字符一定为 '\0';
        char buf[SIZE];
        bzero(buf,SIZE);//写 0 清空
        fgets(buf,SIZE-1,fp)
        buf[SIZE-1]='\0'
gets 的使用 , 编译提示超出缓冲区会导致栈溢出 , 有局限性尽量不使用。
小结 :
        gets 读一行字符串 , 不包含 \n, 不能控制缓冲区溢出;
        fgets 读一行字符串 , 包含 \n, 可控制缓冲区溢出;
        puts 打印一行字符串 , 自带换行符;
        fputs 打印一行字符串 , 不带换行符 , 文件流可输出多行字符串;
直接读写
       
        
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>



/*********************
 * 按区块copy文件
 * xurx
 * 2022/10/12
 **********************/

#define CACHE_SIZE  64


int main(int argc,char ** argv){

    if(argc != 3){
		printf("输入参数不正确\n");
		exit(1);
    }

	char ca[CACHE_SIZE] = {'\0'};

	
	FILE * fb = fopen(argv[1], "r");
    if(fb == NULL){
		printf("打开文件失败\n");
		exit(1);		
    }

    FILE * fw = fopen(argv[2], "w");

    while(!feof(fb) && !ferror(fb))
    {

		bzero(ca, CACHE_SIZE);
		
		fread((char *)ca, 1, CACHE_SIZE-1, fb);
		ca[CACHE_SIZE-1] = '\0';

		int b = strlen(ca);

		if(b>0)
			fwrite((char *)ca, 1, b, fw);
		
	}

	printf("文件copy成功\n");

    

    return 0;
}

        用 fwrite 写 int a=100 到文件 size=4(sizeof(int)) nmemb=1
                 ./7_fwrite a.dat
                hexdump a.dat
                0000000 0064 0000
练习 : char
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    FILE *fp;
    int a=100;
    char b='a';
    char c='b';
    if(argc!=2){
        printf("usage: %s <filename>\n", argv[0]);
    exit(1);
    }
    
    fp = fopen(argv[1], "w");
    if(NULL == fp) {
        perror("fopen fail");
        exit(1);
    }

    fwrite(&a,sizeof(a),1,fp);
    fwrite(&b,sizeof(b),1,fp);
    fwrite(&c,sizeof(c),1,fp);
    
    return 0;
}
$ hexdump b.dat
0000000 0064 0000 6261
0000006

feof()ferror()

可适用于 ASCII 文件和二进制文件
如果读写多次 , 循环多久不做 ?
返回值
        读/ 写成功返回读写成功的数量
        到文件末尾还是出错需要 feof ferror 来决定
        feof 返回值 如果文件结束返回非 0 值否则返回 0
        ferror 返回值 未出错返回 0 , 否则返回非 0 出错
常见的用法:
int ch;
while (!feof(fp) && !ferror(fp)) { //feof(fp) 返回 0 没结束 ferror(fp) 返回 0 没出错
        ch = fgetc(fp);
}
 

关闭文件

        关闭流
        清空缓冲区
        释放缓存空间

标准 IO 小结

缓冲区
        1.无缓冲,perror stderr;
        2.行缓冲,换行符, 正常结束 ,fflush;
        3.全缓冲,缓冲满,正常结束 ,fflush;
读写
        1. 一次读写一个字符 fgetc getc getchar fputc putc putchar;
                结束 EOF (-1);
        2. 一次读写一行 fgets gets fputs puts 结束 NULL;
        3. 一次读写数据块 fread fwrite 结束 feof ferror;
        4. 打开关闭 open fclose;
        第 1,2 种适合 ascii 文本文件 第 3 种适合 文本文件和二进制;

格式化标准输出

fprintf 向文件写数据
        int printf(const char *format, ...);
        //默认向标准输出写数据
        int fprintf(FILE *stream, const char *format, ...);
        //向 stream 指向的文件中写数据
        //const char *format 是常量只读 防止字符串被篡改
sprintf 向内存中写数据
        int sprintf(char *str, const char *format, ...);
        //向内存中写数据
        //方向:format格式化字符串-->str ( 内存 )
        int sprintf(char *str, const char *format, ...);
        //向内存中写数据
        int snprintf(char *str, size_t size, const char *format, ...);
        //向内存中写数据 ,size 防止溢出指定
        把各种不同的类型转换为字符串类型

格式化标准输入

fscanf 从文件读数据
        int scanf(const char *format, ...);
        //默认从键盘获取数据
        int fscanf(FILE *stream, const char *format, ...);
        //从 stream 指向的文件中获取数据
       
        int sscanf(const char *str, const char *format, ...);
        //从内存中获取数据

ftell fseek

        获取文件指针所在的位置
                不会调整文件指针
        设置文件指针的位置
                fseek 设置文件指针的位置 offset whence
                whence:
                        SEEK_SET 文件起始位置
                        SEEK_CUR 文件当前位置
                        SEEK_END 文件末尾
                        为正向后移动
                        为负向前移动
                返回值:
                        成功返回文件位置
                        失败返回-1 设置 errno
实现空洞文件
空洞文件的好处是:空洞文件对多线程共同创作文件是很有用的。因为我们在创建一个很大文件的时候,我们就把一个文件分成很多的段,然后采用多线程的方式,让每个线程负责写入其中的某一段的数据。这样的话比我们用单个线程写入是快很多的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁静的海2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值