【linux系统编程】文件IO

一、系统调用

 二、打开/关闭文件

2.1、open函数

 (1)打开一个存在的文件

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>

int main(int argc, char* argv[])
{
        int fd = open("./leetcode.txt",O_RDONLY);
        printf("fd = %d\n",fd);
        close(fd);
        return 0;
}

(2)打开一个不存在文件

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main(int argc, char* argv[])
{
        int fd = open("leetcode.123",O_RDONLY);
        printf("fd = %d, errno = %d:%s\n",fd,errno,strerror(errno));
        close(fd);
        return 0;
}

 (3)以写方式打开只读文件(权限问题)

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main(int argc, char* argv[])
{
        int fd = open("leetcode.123",O_WRONLY);
        printf("fd = %d, errno = %d:%s\n",fd,errno,strerror(errno));
        close(fd);
        return 0;
}

(4)以只写方式打开目录

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main(int argc, char* argv[])
{
        int fd = open("myDir",O_WRONLY);
        printf("fd = %d, errno = %d:%s\n",fd,errno,strerror(errno));
        close(fd);
        return 0;
}

三、文件描述符

 ulimit -a

 四、read、write函数

read函数

write函数

 4.1、read和write实现cp

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc, char* argv[])
{
        char buff[1024];
        int n = 0;
        // 以只读的方式打开argv[1]
        int fd1 = open(argv[1], O_RDONLY);
        if (fd1 == -1)
        {
                perror("open argv[1] error");
                exit(1);
        }
        // 以读写的方式打开argv[2],如果不存在,则创建,文件权限为 -rw-rw-r--
        int fd2 = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, 0664);

        if (fd2 == -1)
        {
                perror("open argv[2] error");
                exit(1);
        }
        // 从fd1中读数据存到到buff中,每次读1024个,并赋值给n,如果不足1024,读多少就是多少,当读的数据为0时,读完文件
        while ((n = read(fd1, buff, 1024)) != 0)
        {
                if (n < 0)
                {
                        perror("read error");
                        exit(1);
                }
                // 将读的数据以实际大小n从buff中取出,写到fd2中
                write(fd2, buff, n);
        }


        // close
        close(fd1);
        close(fd2);

        return 0;
}

 五、错误处理函数

 六、阻塞/非阻塞

阻塞、非阻塞:  是设备文件、网络文件的属性。

产生阻塞的场景。 读设备文件。读网络文件。(读常规文件无阻塞概念。)

/dev/tty -- 终端文件。

open("/dev/tty", O_RDWR|O_NONBLOCK) --- 设置 /dev/tty 非阻塞状态。(默认为阻塞状态)

6.1、阻塞读

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc, char* argv[])
{
        char buff[10];
        int n = read(STDIN_FILENO, buff, 10);
        if (n < 0) {
                perror("read STDIN_FILENO");
                exit(1);
        }

        write(STDOUT_FILENO, buff, n);
        return 0;
}

 运行程序后,进入等待状态(阻塞),当有输入时,程序才会接着执行,将输入的数据读入,并输出。

6.2、非阻塞超时等待

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<errno.h>
#include<fcntl.h>

#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "time out\n"


int main(void)
{
        char buff[10];
        int fd, n ,i;
        // 设置非阻塞读
        fd = open("/dev/tty", O_RDONLY|O_NONBLOCK);

        if (fd < 0) {
                perror("open /dev/tty");
                exit(1);
        }
        printf("open /dev/tty ok..... %d\n", fd);

        for (int i = 0; i < 5; i++) {
                // 读入数据
                n = read(fd, buff, 10);
                // 有数据读入,跳出循环
                if (n > 0) {
                        break;
                }
                if (errno != EAGAIN) {
                        perror("read /edv/tty");
                        exit(1);
                } else {
                        // 没有数据输入,则输出提示
                        write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
                        sleep(2);
                }
        }
        // i等于5时,循环5次,没有读到数据,等待超时
        if (i == 5) {
                write(STDOUT_FILENO, MSG_TIMEOUT, strlen(MSG_TIMEOUT));
        } else {
                // i != 5,即读到数据,将读到的数据输出
                write(STDOUT_FILENO, buff, n);
        }
        close(fd);
        return 0;
}

 等待超时。

有数据读入,读入数据,并输出,结束程序。

七、lseek函数

 

 文件的读写使用的是同一偏移位置。

7.1、读文件

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
int main(int argc, char* argv[])
{
        int fd, n;
        char msg[] = "It's a test for lseek\n";
        char ch;
        // 以读写的方式打开文件,如果不存在,则创建,权限 -rw-rw-r-
        fd = open("lseek.txt", O_RDWR|O_CREAT, 0664);
        if (fd < 0) {
                perror("open lseek.txt error");
                exit(1);
        }
        // 向文件中写入数据,此时光标在最后
        write(fd, msg, strlen(msg));
        
        // 先注释这行
        //lseek(fd, 0, SEEK_SET);

        while ((n = read(fd, &ch, 1))) {
                if (n < 0) {
                        perror("read error");
                        exit(1);
                }   
                // 将读出的数据在标准输出显示
                write(STDOUT_FILENO, &ch, n);
        }
        close(fd);

        return 0;
}

执行后没有数据输出,因为光标在最后位置。放开注释lseek(fd, 0, SEEK_SET);

 7.2、lseek读文件大小

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
int main(int argc, char* argv[])
{
        int fd = open(argv[1], O_RDWR);
        if (fd == -1) {
                perror("read error");
                exit(1);
        }

        int length = lseek(fd, 0, SEEK_END);
        printf("file size is %d\n",length);
        close(fd);
        return 0;
}

这里要注意lseek函数返回值的意义

 7.3、填充大小

将7.2中的文件从2900填充为3000,查100字节。

修改代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
int main(int argc, char* argv[])
{
        int fd = open(argv[1], O_RDWR);
        if (fd == -1) {
                perror("read error");
                exit(1);
        }
        // 从文件结束位置再偏移100
        int length = lseek(fd, 100, SEEK_END);
        printf("file size is %d\n",length);
        close(fd);
        return 0;
}

下面再用ls命令查看

它的大小不是3000。

原因是,要使文件大小真正拓展,必须引起IO操作。

修改后的扩展文件代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
int main(int argc, char* argv[])
{
        int fd = open(argv[1], O_RDWR);
        if (fd == -1) {
                perror("read error");
                exit(1);
        }

        int length = lseek(fd, 99, SEEK_END);
        printf("file size is %d\n",length);
        write(fd, "$", 1);
        close(fd);
        return 0;
}

 这里2999和3000是因为lseek读取到偏移差的时候,还没有写入最后的‘$’符号。

查看文件

末尾那一大堆^@,是文件空洞,如果自己写进去的也想保持队形,就写入“\0”。

7.4、补充

拓展文件直接使用truncate,简单粗暴

使用 truncate 函数,直接拓展文件。 int ret = truncate("robot.txt", 5000);

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
int main(int argc, char* argv[])
{
        int length = truncate("robot.txt", 5000);
        return 0;
}

八、fcntl函数

 

这个函数超复杂,这里知识简单学习

fcntl用来改变一个【已经打开】的文件的 访问控制属性

重点掌握两个参数的使用, F_GETFL,F_SETFL

fcntl:
	int (int fd, int cmd, ...)
    fd		文件描述符
    cmd		命令,决定了后续参数个数

获取文件状态: F_GETFL

设置文件状态: F_SETFL

8.1、修改属性

终端文件默认是阻塞读的,这里用fcntl将其更改为非阻塞读

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<errno.h>

#define MSG_TRY "try again\n"

int main(int argc, char* argv[])
{
        char buff[10];
        int flags, n;

        flags = fcntl(STDIN_FILENO, F_GETFL);    // 获取stdin的属性信息
        if (flags == 1) {
                perror("fcntl error");
                exit(1);
        }
        // 用或位运算符修改属性
        flags |= O_NONBLOCK;
        // 再将属性赋回去,此时stdin就是非阻塞
        int ret = fcntl(STDIN_FILENO, F_SETFL, flags);
        if (ret == -1) {
                perror("fcntl error");
                exit(1);
        }

tryagain:
        n = read(STDIN_FILENO, buff, 10);
        if (n < 0) {
                // 出错
                if (errno != EAGAIN) {
                        perror("read /dev/tty");
                        exit(1);
                }
                sleep(3);
                write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
                goto tryagain;
        }
        write(STDOUT_FILENO, buff, n);

        return 0;
}

可以看到,是非阻塞读取。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值