UNIXC001 文件操作 之 文件管理案例

1. 文件的打开和关闭

1.1 以只读的方式打开文件

t_file.h

#ifndef T_FILE_H
#define T_FILE_H

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#endif

t_stdio.h

#ifndef T_STDIO_H_
#define T_STDIO_H_
#include <stdio.h>
#define E_MSG(STRING, VAL) do{perror(STRING); return(VAL);}while(0)
#endif
 sudo cp t_file.h t_stdio.h /usr/include/

r_file.c

// #include <sys/types.h>
// #include <sys/stat.h>
// #include <fcntl.h>
// #include <unistd.h>
#include <t_file.h>
#include <t_stdio.h>
int main(int argc, char* argv[]) {
	// 以只读的方式打开文件
    // 返回文件描述符 fd 
    int fd = open(argv[1], O_RDONLY);
    if(fd == -1)E_MSG("open", -1);
    //
    printf("file open success... %s\n", argv[1]);
    // 关闭文件描述符 fd 
    close(fd);
}
$ ls -l *
-rw-rw-r-- 1 moonx moonx    0 12月 10 20:01 tt
-rw-r--r-- 1 moonx moonx    0 12月 10 20:07 tx
--w--w--w- 1 moonx moonx    0 12月 11 16:02 ty

$ gcc r_file.c 
$ a.out tt
file open success... tt
$ a.out tx
file open success... tx
$ a.out ty
open: Permission denied
$ a.out t1
open: No such file or directory

1.2 以写的方式打开文件

  • 文件不存在, 创建文件,指定新文件的权限为0644,如果文件存在,将文件内容清空为0

wt_file.c

#include <t_file.h>
#include <t_stdio.h>
int main(int argc, char* argv[]) {
    // 打开文件, 以写的方式打开文件, 文件不存在创建文件
    // 并指定新文件的权限为0644,如果文件存在, 将文件清空
    int flags = O_WRONLY| O_CREAT| O_TRUNC;
    int mode = 0644;
    int fd = open(argv[1],flags, mode);

    if(fd == -1)E_MSG("open", -1);
    //
    printf("file open success... %s\n", argv[1]);
    close(fd);
}
$ gcc wt_file.c 
$ ./a.out hello
file open success... hello
$ ls hello -l
-rw-r--r-- 1 moonx moonx 0 12月 11 16:46 hello
$ echo 111 > hello  
$ ./a.out hello
file open success... hello
$ cat hello 

  • 文件不存在,创建文件, 指定新文件的权限为0644,如果文件存在报错

we_file.c

#include <t_file.h>
#include <t_stdio.h>
int main(int argc, char* argv[]) {
    // 打开文件, 以写的方式打开文件, 文件不存在创建文件
    // 并指定新文件的权限为0644,如果文件存在, 报错
    // 如果文件存在,O_CREAT 和 O_EXCL一起使用,调用open返回失败
    int flags = O_WRONLY| O_CREAT| O_EXCL;
    int mode = 0644;
    int fd = open(argv[1],flags, mode);

    if(fd == -1)E_MSG("open", -1);
    //
    printf("file open success... %s\n", argv[1]);
    close(fd);
}
$ gcc we_file.c 
$ a.out hello 
open: File exists
$ rm hello 
$ a.out hello 
file open success... hello
$ ls hello 
hello
$ ls -l hello 
-rw-r--r-- 1 moonx moonx 0 12月 11 17:48 hello

2 文件的读写

2.1 read

在这里插入图片描述

  • count 是请求系统去读取count个字节。比如定义的buf有128个字节,count也就同样给128,说明下buf的大小。如果读了130就超过buf的空间了,造成两个字节的浪费。 所以count一般和buf空间的大小一致。
  • 返回值是实际读到的字节数,它可能比count小,这不是错误,仅仅说明已经接近文件末尾。( 比如文件一共有512个字节, 一次读100,连续读了5次,读到500个字节, 第六次读时, 只会读12个字节,返回值是12, 是比count少的,这不代表错误,仅仅代表接近了文件的末尾)
  • read读取到的字节数不包含结束符,而是包含换行符

2.2 write

在这里插入图片描述

  • 把buf里的数据写到跟文件描述符fd相关的那个文件里
  • count也是向系统请求写入文件的的字节数 , buf 里可能有很多字节,count会指定只把buf的某些数据写到文件当中
  • 如果返回值(实际写入的字节数)比count少,这也不代表错误,仅仅代表磁盘满了,再写不进去了

2.3 lseek

在这里插入图片描述

2.4 代码示例

  • cp scr_file dst_file 讲scr_file 的内容复制到dst_file文件当中, 代码实现cp命令功能
    t_cp.c
#include <t_stdio.h>
#include <t_file.h>

//将源文件中的内容复制到目标文件当中
//函数的返回值表示实际拷贝的字节数
int cp_file(int s_fd, int d_fd){
    int total = 0;  //记录世界拷贝的字节数
    // r代表读取到的字节数, 
    // w代表实际写入到目标文件的字节数
    int r,w;
    char buf[128];
    // 从源文件里读出来到buf里的数据,不能保证一次就写到目标文件当中的,
    // 在写的过程当中有可能发生意外,比如被信号打断等
    // 所以这里用了两个个循环来处理意外发生的情况
    while ((r = read(s_fd, buf, 128)) > 0) { //条件为真表示实际读取到了数据
        // 这个tmp的作用是,在第二个while循环中,第二次向目标文件中写入数据时不从buf的的一个字节开始写
        char *tmp = buf;
        while (r > 0) { //保证每次读取到的数据完全写入到目标文件当中
            w = write(d_fd, tmp, r);
            r = r -w; //r 写入之后代表剩下的, r>0 说明本次没有写完,需要继续回去写
            total += w;
            tmp += w;
            // buf += w; //记住buf是数组的名字,常量,这种操作是错的
        }
    }
    return total;
}

// argv[1]表示源文件的名字, argv[2]表示目标文件的名字
int main(int argc, char *argv[]) {

    //框架
    //1. 以只读的方式打开源文件
    int src_fd = open(argv[1], O_RDONLY);
    //2.以写的方式打开目标文件
    //如果文件不存在,创建文件,制定权限为0664. 如果存在将文件内容清空
    int flags = O_WRONLY | O_CREAT | O_TRUNC;
    int dst_fd = open(argv[2], flags, 0644);
    if(src_fd == -1| dst_fd == -1) E_MSG("open", -1);

    //3.将源文件中的内容复制到目标文件当中
    cp_file(src_fd, dst_fd);

    //4.关闭文件描述符
    close(src_fd);
    close(dst_fd);

}
$ gcc t_cp.c
$ ./a.out t_cp.c tt
$ echo hello > tt
$ ./a.out tt tx
$ cat tx 
hello

3 复制文件描述符

3.1 dup

在这里插入图片描述

3.2 dup2

在这里插入图片描述

3.3 案例分析

  • 文件描述符表下标0, 1, 2 分别代表 标准输入, 标准输出, 标准错误输出
  • /home/tarena/tt是新打开的文件(找文件描述符表指针数组中未被使用的最小的下标3, 把struct file类型的对象(files)的地址放进下标对应的存储区里),文件对应的文件描述符是3
  • 如右图,把文件描述符表3里的内容复制到文件描述符表1中,标准输出就输出到 /home/tarena/tt 里(也就是输出重定向
    在这里插入图片描述
  • 图中右边中可以看到,把文件描述符表1中的内容复制(dup)到表中下标最小且未被使用的文件描述符对应的位置, 这时文件描述符4和文件描述符1里的内容就指向了标准输出的sruct_file对象,现在往1和4里写都会写到屏幕上

在这里插入图片描述

  • 图中左边中可以看到, 把文件描述符表项3中的内容复制(dup2,目标文件描述符里有内容)文件描述符表項1中。这时只有4指向了标准输出,1和3都指向打开的tt对应struct_file类型对象(这个对象里的引用计数就是2了)。这时往1和3里写都是一样的效果, 会写入tt。
  • 图中右边可以看到,关闭文件描述符表3,然后就只有1里的地址指向tt对应的struc_file对象。到这里就完成文件描述符的复制,也就改变了进程的输出的方向
    在这里插入图片描述
  • 如图中左边所示,把文件描述符表项4的内容又拷贝(dup2)到1中,现在就没有文件描述符指向tt对应的struct_file。,然后再close(4)就到了图中右边。这样就又恢复了原来的标准输出,输出到显示器。

3.4 代码示例

#include <t_stdio.h>
#include <t_file.h>
#include <string.h>
// 重定向文件名通过argv[1]
int main(int argc, char *argv[]) {
    char *msg = "this is a test...\n";

    // 图一
    int flags = O_WRONLY | O_CREAT | O_TRUNC;
    int fd = open(argv[1], flags, 0644); //fd ==3
    if(fd == -1) E_MSG("open", -1); 
    int s_fd = dup(1); //讲标准输出保存到sfd中,s_fd ==4

    // 图二
    //将打开的文件描述符,复制到标准输出
    // 如果1是打开的,先将1关闭,再把fd考过去
    dup2(fd, 1);
    //关闭文件描述符
    close(fd);
    // 通过标准输出将信息输出到文件 
    write(1, msg, strlen(msg));

    // 图三
    //恢复标准输出,重新指向显示器
    dup2(s_fd, 1);
    close(4);
    write(1, msg, strlen(msg));
}
moonx@moonx:~/Desktop/C/c++$ gcc direct.c 
moonx@moonx:~/Desktop/C/c++$ ./a.out 11
this is a test...
moonx@moonx:~/Desktop/C/c++$ cat 11 
this is a test...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值