Linux基础IO - 系统接口

本文中我们将讲述如下的内容,C文件中IO相关操作,认识文件相关系统调用接口。

首先我们来通过一些简单的代码简要的回顾C文件接口:

语言方案

写文件

// 写文件
#include 
#include 
int main()
{
    FILE *fp = fopen("myfile", "w");
    if(!fp){
        printf("fopen error!\n");
    }
    const char *msg = "hello world!\n";
    int count = 5;
    while(count--){
        fwrite(msg, strlen(msg), 1, fp);
    }
    fclose(fp);
    return 0;
}

读文件

// 读文件
#include 
#include 
int main()
{
    FILE *fp = fopen("myfile", "r");
    if(!fp){
        printf("fopen error!\n");
    }
    char buf[1024];
    const char *msg = "hello world!\n";
    while(1){
    //注意返回值和参数,此处有坑,仔细查看man手册关于该函数的说明
        ssize_t s = fread(buf, 1, strlen(msg), fp);
        if(s > 0){
            buf[s] = 0;
            printf("%s", buf);
        }
        if(feof(fp)){
            break;
        }
    }
    fclose(fp);
    return 0;
}

打开文件的方式还有很多比如"w"、"r"、"a"、"w+"...可以通过不同的方式来对文件进行处理。

上述的代码都是语言级别的方案,下面我们将要来介绍一些系统方案:

系统方案

首先看一下打开文件的方法open和它的返回值:

#include 
#include 
#include 

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

pathname: 要打开或创建的目标文件

flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。

参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写

返回值:
成功:新打开的文件描述符
失败:-1

可以看到open函数会返回一个被称为文件描述符的整数值,下面就来演示一下open函数的用法,这个函数的第一个参数表示,文件的路径与文件名, 第二个参数flags有很多的选项,在讲述这个问题之前,首先要了解一些OS一般如何让用户给自己传递标志位。

OS传递标志位

在我们自己编写的程序中,一般我们会给函数传递不同的参数来表示不同的含义,例如 int func(int flag) // flag = 1、2、3,当我们想要传递多个标志位的时候,在函数中不可能传入很多个形参,这非常的不便捷。因为一个整数有四个字节,32个比特位,系统就会让一个或几个比特位表示一个标志位,这样一个int就可以最多表示32个标志位,这样就足够使用了,这种数据结构就是之前学习过的位图。通过一个简单地demo就可以了解标志位的作用:

#include 

#define ONE 0X1
#define TWO 0x2
#define THREE 0x4
#define FOUR 0x8
#define FIVE 0x10

void Print(int flags)
{
	if (flags & ONE) printf("hello 1\n"); // 充当不同的行为
	if (flags & TWO) printf("hello 2\n");
	if (flags & THREE) printf("hello 3\n");
	if (flags & FOUR) printf("hello 4\n");
	if (flags & FIVE) printf("hello 5\n");
}

int main()
{
	printf("---------------------------\n");                                                                   
	Print(ONE); // 传入不同的指令参数
	printf("---------------------------\n");
	Print(TWO);
	printf("---------------------------\n");
	Print(FOUR);
	printf("---------------------------\n");
	Print(ONE|THREE);
	printf("---------------------------\n");
	Print(ONE|THREE|FIVE);
	printf("---------------------------\n");
	Print(ONE|TWO|THREE|FOUR|FIVE);
	printf("---------------------------\n");
}

然后就可以得到这样的结果,非常的显而易见通过在一个形参内传入不同的指令得到不同的结果:Linux基础IO - 系统接口_第1张图片

open函数中的flags也就是这样的效果。

open()

#define LOG "log.txt"
int main()
{
	int fd = open(LOG, O_CREAT | O_WRONLY); // O_CREAT 表示若文件不存在就创建文件 O_WRONLY 表示读取文件                                                                
	if (fd == -1)                                                             
	{                                                                         
		printf("fd:%d, errno:%d, errstring:%s\n", fd, errno, strerror(errno)); // 输入对应的fd,错误码和打印错误码描述
	}                                                                         
	printf("fd:%d, errno:%d, errstring:%s\n", fd, errno, strerror(errno));    
																				
	close(fd);                                                                
	return 0;                                                                 
}                 

O_CREAT

如果没有O_CREAT 就会打印出这样的错误:

添加了O_CREAT之后就可得到正确的结果:

 

umask()

但是查看文件会发现出现了这样的现象,因为创建文件是需要对应的权限的,而在函数中我们没有基于权限,这时就需要用到open的第二个带有权限的函数:

Linux基础IO - 系统接口_第2张图片

umask(0)
int fd = open(LOG, O_CREAT | O_WRONLY, 0666);   

再次查看文件确实生成的需要的文件,但是这个文件的权限并不是我们设立的666,因为文件权限会受到umask的影响,可以调用umask来对其进行修正。

Linux基础IO - 系统接口_第3张图片Linux基础IO - 系统接口_第4张图片

O_TRUNC

清空文件:

当我们对消息的内容进行修改的时候可以发现,输入了新的内容之后旧的内容没有删除Linux基础IO - 系统接口_第5张图片

这说明原有的参数方法不够,需要在添加O_TRUNC来清空文件,编译后再次运行得到: 

// O_CREAT | O_WRONLY 默认不会对原始文件内容清空
int fd = open(LOG, O_CREAT | O_WRONLY | O_TRUNC, 0666);

 

O_APPEND

追加写:

 int fd = open(LOG, O_WRONLY | O_CREAT | O_APPEND, 0666); // 只要进行写入就需要 O_WRONLY 若是没有文件就需要 O_CREAT ,最后是写文件的策略 O_APPEND 追加写

write()

下面简要的介绍write接口,和部分相关的代码:

// 写入 
const char* msg = "hello world";
int cnt = 5;
while (cnt)
{
	char line[128]; // 缓冲区                                                                              
	snprintf(line, sizeof(line), "%s, %d\n", msg, cnt); // 格式化写入到line中
                                     
	write(fd, line, strlen(line) + 1);// 这里的strlen不需要+1,\0是C语言中的规定,不是文件的规定!\0解释出来会变成乱码
	write(fd, line, strlen(line));     
                                     
	// write(fd, msg, strlen(msg));    
                                                                                                
  cnt--;                                                                                        
}   

 这里我们会遇到这样的问题,strlen是否需要+1,假设先让其+1,经过编译可以得到输出是正常的,但是当通过vim查看时可以发现出现了错误:Linux基础IO - 系统接口_第6张图片

Linux基础IO - 系统接口_第7张图片因为0在ascii码表属于不可显字符,不会打印出来。

read()

下面简要的介绍read接口,和部分相关的代码:

系统的接口读取与写入文件是没有/0的,而C语言是有'/0'的,在系统与C语言进行交互的时候需要注意'\0'的问题。

int fd = open(LOG, O_RDONLY);

// 读取
char buffer[1024];
// 这里我们无法做到按行读取,这里是整体读取的                                                              
ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // 使用系统接口来进行I/O时,一定要注意\0的问题
if (n > 0)          
{                   
	buffer[n] = '\0'; 
	printf("%s\n", buffer);
}     

你可能感兴趣的:(Linux,c语言,linux)