Linux系统编程(二)---学习Linux系统函数

目录

一、Linux系统介绍

二、标准IO

1.文件信息介绍:

2.打开文件:

操作系统默认打开了三个流:

3.关闭文件:

这里介绍一下perror和printf的区别:

4.按字符读写:

cat函数:

cp函数:

5.按字符串读写:

cp函数:

cat函数:

记录文件行数:

注意:

6.按对象读写(二进制流读写)

用途:在结构体上使用这个函数读写会更便利:

cat函数:

cp函数:

7.文件定位:

移动文件位置指针

获取文件位置指示器

回到文件开头

8.格式化输入输出

讲解一下缓冲区机制:

注意:

查看缓冲区大小:

9.错误处理

三、总结


一、Linux系统介绍

linux本质上是个软件,linux系统分了内核空间和用户空间,如果要使用到linux系统的功能需要通过系统调用来使用到,系统调用就是指linux系统本身提供的函数

linux系统下,实现一个用户的应用程序:

两条路径:

1.直接通过系统调用直接实现

2.通过库的方式实现---函数库---一般是别人基于linux系统调用实现的功能更丰富使用更方便的函数

下面讲解linux本身提供的函数:标准IO

二、标准IO

首先介绍文件---一组相关信息的集合

linux将很多东西都抽象成文件

1.文件信息介绍:

- 普通文件(常规文件) 例:1.txt 图片 视频 音频 可执行文件ls -l 1.txt

d 目录文件(directory) 例:文件夹 目录 ls -l 目录

c 字符设备文件(char) 例:某一个硬件---鼠标,键盘,摄像头等 ls -l/dev/input

b 块设备文件(block) 例:硬盘等存储设备---512Bytes ls -l /dev/sda

l 软连接文件(link) 例:相当于Windows下的快捷方式 ls -l /dev/lib //浅蓝色

s 套接字文件 例:主要指的是本地socket---进程间通信

p 管道文件(pipe) 例:进程间通信 mkfifo pipe

ls -l pipe

2.打开文件:

FILE *fopen(const char *pathname, const char *mode);

参数说明:

pathname : 文件路径(相对或绝对)

mode : 打开模式

模式 说明 文件不存在 文件存在

"r" //只读 失败 从头读

"r+" //读写 失败 从头读写
"w" //只写 创建 清空内容

"w+" //读写 创建 清空内容
"a" //追加 创建 追加到末尾
"a+" //读写 创建 追加,可读任意位置

返回值:

成功: 返回 FILE * 指针

失败: 返回 NULL

示例代码:

复制代码
FILE *fp = fopen("1.txt", "r");//在当前目录打开一个1.txt文件
if (fp == NULL) {
perror("fopen fail"); // 打印错误信息
return -1;
}

简要介绍一下流的概念:

操作系统默认打开了三个流:

stdin //标准输入 //FILE*指针 //获取键盘输入的值

键盘上ctrl+d可以产生一个EOF

stdout //标准输出,输出到终端

stderr //标准出错//也是个输出

3.关闭文件:

int fclose(FILE *stream); //fclose(fp);

作用: 刷新缓冲区并关闭文件

返回值: 成功返回 0,失败返回 EOF(-1)

注意: 必须关闭文件,否则数据可能丢失!

这里介绍一下perror和printf的区别:

注意看缓冲区,主要是这里的区别!

一个是无缓冲,一个是行缓冲,不懂就去了解下面的缓冲区机制

4.按字符读写:

int fgetc(FILE *stream); //读

int fputc(int c, FILE *stream); //写

功能:从流中获得数据(一次只能读取一个字符)

返回值:

成功:返回读取到的字符的数值

失败:返回EOF(-1)当到达文件结尾也会返回EOF

练习:写一个cat和cp函数:

cat函数:

复制代码
#include <stdio.h>

// ./a.out 1.txt 
int main(int argc, const char *argv[])
{
	//命令行参数的处理 
	if (argc != 2)
	{
		printf("Usage: %s <filename>\n",argv[0]);
		return -1;
	}

	//1.打开文件 
	FILE *fp = fopen(argv[1],"r");
	if (fp == NULL)
	{
		//printf("fopen fail!\n");
		perror("fopen fail");
		return -1;
	}
	//2.读取 
	int ret = 0;

	while ((ret = fgetc(fp)) != EOF)
	{
		//printf("%c",ret);
		//printf("%c",ret);
	    fputc(ret,stdout);
	}

	//3.关闭 
	fclose(fp);
	//fclose(fp);
	
	return 0;
}

cp函数:

复制代码
#include <stdio.h>


//./a.out 1.txt 2.txt 
int main(int argc, const char *argv[])
{
	if (argc != 3)
	{
		printf("Usage: %s <src> <dest>\n",argv[0]);
		return -1;
	}

	//1.打开 
	FILE *fp_s = fopen(argv[1],"r");
	FILE *fp_d = fopen(argv[2],"w");
	if (fp_s == NULL || fp_d == NULL)
	{
		perror("fopen fail ");
		return -1;
	}

	//2.读写 
	int ret = 0;

	while ((ret = fgetc(fp_s)) != EOF)
	{
		fputc(ret,fp_d);
	}

	//3.关闭 
	fclose(fp_s);
	fclose(fp_d);
	
	return 0;
}

5.按字符串读写:

char *fgets(char *s, int size, FILE *stream); //读

重要特性:
最多读取 size - 1 个字符
遇到换行符 \n 会停止,换行符也会被读入被保存到s指定的空间

自动在末尾添加 \0

返回值: 成功返回 s ,失败/EOF 返回 NULL
int fputs(const char *s, FILE *stream); //写

将s中的字符串输出到流中

不会自动添加换行符

返回值: 成功返回非负数,失败返回-1

练习:写一个cp函数和cat函数,和记录文件行数的函数

cp函数:

复制代码
#include <stdio.h>

void do_copy(FILE *fp_s,FILE *fp_d)
{
	//2.读写 
	char s[1024] = {0};
	while (fgets(s,sizeof(s),fp_s) != NULL)
	{
		fputs(s,fp_d);
	}

}


//./a.out 1.txt 2.txt 
int main(int argc, const char *argv[])
{
	if (argc != 3)
	{
		printf("Usage: %s <src> <dest>\n",argv[0]);
		return -1;
	}

	//1.打开 
	FILE *fp_s = fopen(argv[1],"r");
	FILE *fp_d = fopen(argv[2],"w");
	if (fp_s == NULL || fp_d == NULL)
	{
		perror("fopen fail ");
		return -1;
	}

	//2.拷贝 
	do_copy(fp_s,fp_d);

	//3.关闭 
	fclose(fp_s);
	fclose(fp_d);
	
	return 0;
}

cat函数:

复制代码
#include <stdio.h>

int main(int argc, const char *argv[])
{
    if(argc != 2)
    {
        printf("Usege : %s <filename>\n",argv[0]);
        return -1;
    }
    FILE *fp = fopen(argv[1],"r");
    if(fp == NULL)
    {
        perror("fail");
        return -1;
    }
    char ret[100] ={0};
    while(fgets(ret,sizeof(ret),fp)!=NULL)                 
    {
        printf("%s",ret);
    }
    fclose(fp);
    return 0;
}

记录文件行数:

复制代码
 #include <stdio.h>
 #include <string.h>
 int main(int argc, const char *argv[])
 {
     if(argc != 2)                                              
     {
         printf("Usege : %s <filename>\n",argv[0]);
         return -1;
     }
     FILE *fp = fopen(argv[1],"r");
     if(fp == NULL)
     {
         perror("fail");
         return -1;
     }
     char ret[100] ={0};
     int cnt = 0;
     while(fgets(ret,sizeof(ret),fp)!=NULL)
     {
         if(ret[strlen(ret)-1] == '\n')
         {
             ++cnt;
         }
     }
     printf("%s has %d line\n",argv[1],cnt);
     fclose(fp);
     return 0;
 }

注意:

fputs 如果要输出的数据中没有换行,它不会自己输出换行

fputs要输出的是字符串,遇到\0结束

puts默认会输出换行
fgetc和fputc拷贝普通文件和二进制都没问题,但是文件大时,效率不高

fgets和fputs拷贝普通文件没问题,二进制有问题(\0)

6.按对象读写(二进制流读写)

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); //读

参数说明:

ptr : 接收数据的缓冲区 //char s[100]

size : 每个数据项的大小(字节) //一般是sizeof(char)

nmemb : 要读取的数据项个数 //一般是sizeof(s)

返回值: 实际读取的数据项个数
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); //写

ptr : 接收数据的缓冲区 //char s[100]

size : 每个数据项的大小(字节) //一般是sizeof(char)

nmemb : 要写入的数据项个数 //一般是sizeof(s)

返回值: 实际读取的数据项个数

用途:在结构体上使用这个函数读写会更便利:

例如:

cs 复制代码
typedef struct {
char name[20];
int sno;
float score;
} Student;

Student students[3] = {
{"Tom", 110, 99.5},
{"Jerry", 111, 99.9},
{"Jack", 112, 100}
};

// 写入 3 个学生记录
fwrite(students, sizeof(Student), 3, fp);

// 读取 1 个学生记录
Student s;
fread(&s, sizeof(Student), 1, fp);

练习:写一个cp函数和cat函数:

cat函数:

注意,如果用printf来输出到终端的话,需要加一个'/0',且读取元素的个数需要时sizeof(buf)-1,因为该函数不会自己补'\0',且会把缓冲区直接读满,所以需要-1给数组留一个位置加一个'\0'进去

cs 复制代码
#include <stdio.h>

int main(int argc, const char *argv[])
{                                                                   
    if(argc != 2)
    {
        printf("Usege : %s <filename>\n",argv[0]);
        return -1;
    }
    FILE *fp = fopen(argv[1],"r");
    if(fp == NULL)
    {
        perror("fail");
        return -1;
    }
    int ret = 0;
    char buf[100];
    while((ret = fread(buf,sizeof(char),sizeof(buf),fp)) != 0)
    {
    //  buf[ret] ='\0';
    //  printf("%s",buf);
        fwrite(buf,sizeof(char),ret,stdout);
    }

    fclose(fp);
    return 0;
}

cp函数:

cs 复制代码
#include <stdio.h>                                               

int main(int argc, const char *argv[])
{
    if(argc != 3)
    {
        printf("Usege : %s <filename> <filename>\n",argv[0]);
        return -1;
    }
    FILE *fp = fopen(argv[1],"r");
    if(fp == NULL)
    {
        perror("fail");
        return -1;
    }

    int ret =0;
    FILE *fp2 = fopen(argv[2],"w");
    if(fp2 == NULL)
    {
        perror("fail");
        return -1;
    }
    char s[100]={0};
    while((ret = fread(s,sizeof(char),sizeof(s),fp))!= 0)
    {
        fwrite(s,sizeof(char),ret,fp2);
    }

    fclose(fp);
    fclose(fp2);
    return 0;
}
                                                                 

7.文件定位:

移动文件位置指针

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

参数说明:

offset : 偏移量(可正可负)

whence : 起始位置
SEEK_SET //文件开头
SEEK_CUR //当前位置
SEEK_END //文件末尾

返回值: 成功返回 0,失败返回 -1

cs 复制代码
// 移动到文件开头
fseek(fp, 0, SEEK_SET);
// 移动到文件末尾
fseek(fp, 0, SEEK_END);
// 向前移动 10 字节
fseek(fp, -10, SEEK_CUR);
// 移动到第 100 个字节
fseek(fp, 100, SEEK_SET);

获取文件位置指示器

long ftell(FILE *stream);

返回值: 当前位置相对于文件开头的偏移量

cs 复制代码
//示例
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
printf("文件大小: %ld 字节\n", file_size);

回到文件开头

void rewind(FILE *stream);

等价于fseek(fp, 0, SEEK_SET)

8.格式化输入输出

int printf( const char *format, ...);//格式化输出到终端

int fprintf(FILE *stream, const char *format, ...);//格式化输出到文件

int sprintf(char *str, const char *format, ...);//格式化输出到数组

示例:

cs 复制代码
#include <stdio.h>


int main(int argc, const char *argv[])
{
    printf("%d:hello\n",2026);
    fprintf(stdout,"%d:hello\n",2026);
    char s[100];
    sprintf(s,"%d:hello\n",2026);
    printf("%s",s);                            


    return 0;
}
                                               

讲解一下缓冲区机制:

行缓存---一般与终端关联---1024字节

特点(什么时候会被刷新?)

1.遇到/n

2.缓冲区满

3.程序正常退出(1.从main函数返回 2.exit函数)

4.fflush函数 //强制刷新
全缓存 ---与普通文件关联---4096字节

特点(什么时候会被刷新?)

1.遇到/n

2.缓冲区满

3.程序正常退出(1.从main函数返回 2.exit函数)

4.fflush函数 //强制刷新
不缓存 ---错误信息的输出设计为不缓存

注意:

这里的fprintf就是写入文件就是全缓存,必须使用fflush(fp)冲刷才可以写入,不然没有输出结果

printf如果看不到结果也可能是因为程序没结束,或者没加\n

int fflush(FILE *stream); //冲刷缓冲区

cs 复制代码
printf("加载中...");
fflush(stdout); // 立即显示,不等换行符

查看缓冲区大小:

cs 复制代码
// 需要先触发缓冲区分配(如调用 getchar())
printf("stdin 缓冲区: %ld 字节\n",
stdin->_IO_buf_end - stdin->_IO_buf_base);
printf("stdout 缓冲区: %ld 字节\n",
stdout->_IO_buf_end - stdout->_IO_buf_base);
printf("stderr 缓冲区: %ld 字节\n",
stderr->_IO_buf_end - stderr->_IO_buf_base);

9.错误处理

int feof(FILE *stream); //检测文件结束 文件指针达到EOF返回真

int ferror(FILE *stream); //检测错误 文件指针不在EOF或者出错则返回真

void perror(const char *s); //打印错误信息

三、总结

字符fgetc() 、fputc() 效率低,简单

字符串fgets() 、fputs() 按行处理

二进制块fread() 、fwrite() 效率高,通用
要理解的概念:

1. FILE 结构体: 封装了文件描述符和缓冲区

2. 系统函数的功能和格式

3. 缓冲区机制: 提高 I/O 效率的关键

4. 文件指针: 记录当前读写位置

相关推荐
Gofarlic_OMS14 分钟前
Windchill的license合规使用报告自动化生成与审计追踪系统
大数据·运维·人工智能·云原生·自动化·云计算
迷途之人不知返18 分钟前
shell相关知识与Linux权限
linux
SPC的存折21 分钟前
3、主从复制实现同步数据过滤
linux·运维·服务器
SPC的存折23 分钟前
openEuler 24.03 MariaDB Galera 集群部署指南(cz)
linux·运维·服务器·数据库·mysql
xcbrand25 分钟前
文旅行业品牌策划公司找哪家
大数据·运维·人工智能·python
SPC的存折37 分钟前
MySQL 8.0 分库分表
linux·运维·服务器·数据库·mysql
才知道的1 小时前
stm32F407学习DAY.27 ADC
stm32·嵌入式硬件·学习
cyber_两只龙宝1 小时前
【Oracle】Oracle之DQL中WHERE限制条件查询
linux·运维·数据库·云原生·oracle
Orange_sparkle1 小时前
learn claude code学习记录-S02
java·python·学习
小郑加油1 小时前
python学习Day1:python的安装与环境搭载
python·学习·小白记录,保姆式教程