linux 之文件基础 (二)、文件IO API

1. 操作文件的重要性

由于linux的一切皆文件的概念,因此,学会如何操作文件是非常重要的。对于操作文件,我们需要掌握以下内容:

  • 基本的文件操作: 以特定的权限打开文件、关闭文件、向文件读写等。
  • 操作文件的属性:获取属性、改变属性等。
  • 对于目录文件的操作

上述的每一类操作都有对应的API。 下面我们分别介绍各种API

2. 文件基本操作之 文件IO

2.1 文件IO 简介

文件IO,是Linux上操作文件的两套API集合之一,另外一套API是标准IO。二者的区别是是否自带缓冲。“没有自带缓存的I/O”就是文件IO。需要注意,文件IO的API 属于系统调用,因此在查看帮助手册时应该查第2分册。

2.2 文件IO之打开文件 open 函数

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

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

2.2.1函数参数

(1)pathname : 需要打开文件的文件名
(2)flags : 打开方式,可选方式有以下几种

  • O_RDONLY:只读方式打开文件。
  • O_WRONLY:可写方式打开文件。
  • O_RDWR:读写方式打开文件。
  • O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限(读写执行权限)。
  • O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。
  • O_TRUNC:如文件已经存在,那么打开文件时先删除文件中原有数据。
  • O_APPEND:以添加方式打开文件,所以对文件的写操作都在文件的末尾进行write写文件默认从开始位置开始写,加了这个参数以后从文件末尾开始写。

(3)mode : 指定文件权限 也可以用宏实现

  • S_IRWXU 00700 user (file owner) has read, write, and execute
    permission
  • S_IRUSR 00400 user has read permission
  • S_IWUSR 00200 user has write permission
  • S_IXUSR 00100 user has execute permission
  • S_IRWXG 00070 group has read, write, and execute permission

2.2.2 函数返回值:

  • 成功:返回文件描述符
  • 失败:-1

2.2.3 使用举例子

在这里插入图片描述

open 不能创建 设备文件,只能创建普通文件。

int creat(const char *pathname, mode_t mode);
等价于open(pathname, O_CREAT|O_WRONLY|O_TRUNC, mode)

2.3 关闭文件

2.3.1 close 函数原型

#include <unistd.h>
int close(int fildes);

2.3.2 函数参数:

  • fildes : 需要关闭文件的文件描述符

2.3.3 返回值:

  • 成功 0
  • 失败 -1

注意:由于一个进程只能打开1024 个文件描述符,因此,不用的文件应该及时的关闭。
当一个进程终止时,内核会将打开的所有的文件描述符关闭。
释放一个文件的同时,也释放该进程在该文件上的所有记录锁。

2.4 文件IO 之读文件

2.4.1 read 函数原型

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

函数功能:
从一个已经打开的文件中读取内容

2.4.2 函数参数

fd : 文件描述符
buf : 用来存放读到的内容的缓冲区首地址
count :读取的字节数

2.4.3 返回值

成功: 返回实际读取的字节数(0 读到文件末尾)
失败: -1 并设置errno

2.5 写函数

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

函数功能:
向已经打开的文件中写内容

2.5.1函数参数:

fd : 文件描述符
buf : 写缓冲缓冲区首地址
count : 指定写入的字节数

2.5.2 返回值:

  • 成功:实际写到文件的字节数
  • 失败:-1

注意:

  • 写文件出现错误的原因通常是,磁盘已满、或者文件长度超过了一个进程文件长度限制。
  • 对于普通文件,写操作从当前文件偏移量开始
  • 在打开文件时,如果指定了O_APPEND 参数,那么执行写操作从文件末尾追加。

2.5 文件定位函数

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

函数功能:文件定位。 实际上就是调整文件内部的指针的,在读和写的时候,每读一个或者写一个字节,文件指针就会自动向后移动一个。

2.5.1 函数参数:

(1)fd:文件描述符
(2)offset:相对于偏移量偏移量,每一读写操作所需要移动的距离,单位是字节的数量,可正可负(对应:向前移,向后移
(3)whence: 基准点。可选项有一下几个

  • SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小。
  • SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量。
  • SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小。

2.5.2 返回值:

  • 失败 :-1
  • 成功:当前文件的偏移量(当前读写位置相对文件开始位置的字节数)通过lseek计算文件的大小

注意:

  • buf 需要调用者自己来分配内存,使用后,由调用者自己释放。
  • 读操作从文件的当前偏移量开始,在读成功返回前,读多少字节,将稳健偏移量加多少。
  • 向前偏移不可以偏移超过文件的头
  • 向后偏移,如果超过了文件的尾,那么会形成文件空洞。
  • 空洞文件和真实文件的区别在于磁盘的使用情况 。通过du -sh 文件名可以 查看空洞文件和真实文件的区别,这个命令显示文件真实使用了多少的磁盘空间。

空洞文件的作用:

空洞技术非常有用。例如迅雷下载一个3G的电影,当我们点击下载后,会发现下载的目录里面会多出一个3G的文件,这3G文件实际上就是空洞文件。意思是迅雷先占了3G的空间。后面下载的内容往空洞里面填。

3. 文件IO练习

3.1 打开一个文件

/*
以0644 权限 打开test.txt 文件,如果文件不存在则创建文件,如果文件存在则截短文件内容。
*/
#include<stdio.h>                                                                                                                              
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    int fd = open("test.txt",O_WRONLY | O_CREAT | O_TRUNC,0644);
    if(fd == -1) 
    {   
        perror("open file error");
    }   
    return 0;
}

3.2 计算一个文件中有多少个字符

#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
 *读一个文件打印,并计算一个文件中有多少个字符。
 * */
int
main()
{
    int fd = open("file.txt",O_RDONLY,0666);
    if(fd == -1) 
    {   
        perror("open file error");
        return -1; 
    }   
    char read_buf[128];
    int count = 0;
    int sum = 0;
    while((count = read(fd,read_buf,5))>0)
    {   if(count != 5)
        {   
            read_buf[count] = '\0';// 如果count 不等于5, 则需要把第count个字符以后的缓冲区清空。
        }   
        printf("%s",read_buf);
        sum += count;
    }   
    printf("一共%d个字符\n",sum);
    close(fd);
    return 0;
}   

3.3 实现简单的cp 命令

/*                                                                                                                                             
 *cp file1 file2  将file1 的内容拷贝到file2 中, file1  file2 由命令行指定
 * */
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main( int argc , const char * argv[])
{
    if(argc != 3)
    {
        perror(" 参数错误 ");
        return -1;
    }
    //打开文件1
    int file1_fd = open(argv[1],O_RDONLY,0666);
    if(file1_fd == -1)
    {
        perror("open file1 error");
        return -1;
    }
    //打开文件2,如果文件2 不存在,则新创建一个,如果已经存在则截断
    int file2_fd = open(argv[2],O_RDWR | O_CREAT | O_TRUNC,0666);
    if(file1_fd == -1)
    {
        perror("open file2 error");
        return -1;
    }
    char buf[128] = "\0";
    int readCount;
    while((readCount = read(file1_fd,buf,5)) > 0)
    {
        write(file2_fd,buf,readCount);
    }
    close(file2_fd);
    close(file1_fd);
    return 0;
}   

查看两个文件内容是否一致命令:
diff -s file1 file2

posted @ 2020-03-30 00:27  江南又一春  阅读(450)  评论(0编辑  收藏  举报