I/O day3

本文详细介绍了C语言中用于文件操作的函数,如fseek、ftell和rewind用于管理文件光标,以及open、read、write、close和lseek等系统调用进行文件IO。同时,讨论了文件描述符的概念和使用,包括umask、dup函数在文件描述符复制中的作用。文章还提供了示例代码,演示如何读取、写入和操作文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

十、关于光标位置相关的函数(fseek、ftell、rewind)

      #include <stdio.h>

       int fseek(FILE *stream, long offset, int whence);
功能:修改文件光标位置
参数1:要修改光标的文件
参数2:偏移量
        >0:表示向后偏移
        =0:不偏移
        <0:表示向前偏移
 参数3:偏移的起始位置
         SEEK_SET:文件的开头
         SEEK_CUR:当前位置
         SEEK_END:文件结尾
返回值:成功返回0,失败返回-1并置位错误码        
eg:    fseek(fp, 10, SEEK_SET);          //从开头向后移动10个字节
       fseeke(fp, 10, SEEK_CUR);       //从当前位置向后移动     
       fseeke(fp, -10, SEEK_CUR);       //从当前位置向前移动
       fseek(fp, 0, SEEK_END);         //将光标定位到结尾                     
                              

       long ftell(FILE *stream);
功能:获取光标当前位置
参数:文件指针
返回值:成功返回文件当前位置,失败返回-1,置位错误码
eg:
        fseek(fp,0,SEEK_END);
        ftell(fp);                     ===>这两条语句相当于求出文件大小              

       void rewind(FILE *stream);
功能:将光标定位到开头
参数:文件指针
返回值:无
        等价于:fseek(fp, 0, SEEK_SET)       

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

int main(int argc, const char *argv[])
{
    FILE *fp;
    char buf[10] = "";

    //以读写的形式打开文件
    if((fp=fopen("./Test.txt", "w+")) == NULL)
    {
        perror("open file ");
        return -1;
    }

    //向文件中写入数据
    fprintf(fp, "hello world\n");
    fprintf(fp, "Good morning...\n");

    
    //将光标移动到开头
    //fseek(fp, 0, SEEK_SET);//将光标定位在从开始偏移0字节
    fseek(fp, 12, SEEK_SET);//将光标定位在从开始偏移0字节

    //读取数据
    fgets(buf, sizeof(buf), fp);

    //关闭文件
    fclose(fp);


    printf("buf = %s\n", buf);

    return 0;
}

 练习:通过代码读取一张图片,对图片进行操作

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

int main(int argc, const char *argv[])
{
    FILE *fp;

    //以r+的形式打开文件
    if((fp = fopen("./milaoshu.bmp", "r+")) == NULL)
    {
        perror("open file");
        return -1;
    }

    //想要获取文件大小
    unsigned int img_size;
    //将文件光标偏移2字节,前两字节是文件类型
    fseek(fp, 2, SEEK_SET);

    //读取文件大小
    fread(&img_size, 4, 1, fp);

    //输出图片大小
    printf("img_size = %d\n", img_size);

    //向后偏移54字节,得到数据
    fseek(fp, 54, SEEK_SET);

    //定义一个颜色
    unsigned int color[3] = {0, 0xff, 0};
                            //蓝色 绿色 红色

    //遍历图片
    for(int i=0; i<100; i++)
    {
        for(int j=0; j<500; j++)
        {
            fwrite(color, 4, 3, fp);
        }
    }



    //关闭文件
    fclose(fp);

    return 0;
}

思考:如果以追加的方式打开文件,使用fseek能不能更改光标?

如果以“a”的形式进行追加,fseek不能修改光标的位置

如果以“a+”的形式追加,fseek不能修改写指针的光标,但是可以更改读指针的光标

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

int main(int argc, const char *argv[])
{
    FILE *fp;
    //以追加的形式打开文件
    
    //if((fp = fopen("./Test.txt", "a")) == NULL)
    if((fp = fopen("./Test.txt", "a+")) == NULL)
    {
        perror("open file");
        return -1;
    }

    //将指针定位到开头
    fseek(fp, 12, SEEK_SET);

    char buf[10] = "";
    fread(buf, 1, sizeof(buf), fp);
    printf("buf = %s\n", buf);
    //fprintf(fp,"lalalala...\n");

    //关闭文件
    fclose(fp);

    return 0;
}

 

十一、文件IO

基于系统调用的方式,只要调用文件IO的接口,程序就会从用户空间向内核空间进行一次交互,效率相比于标准IO较低,原因是没有缓冲区

接口:open、read、write、close、lseek。。。

11.1 文件描述符

1> 文件描述符是一个大于等于0的非负整数,使用open打开一个文件后,就会返回该文件的描述符,在之后想要操作文件,只需通过该文件描述符即可

2> 在一个正在执行的程序中,默认打开了三个文件描述符0(stdin)、1(stdout)、2(stderr)

3> 文件描述符的使用原则:最小未分配原则

4> 一个程序中的文件描述符的最大承载量默认为1024

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

int main(int argc, const char *argv[])
{
    //默认提供的文件指针对应的文件描述符
    printf("fileno of stdin = %d\n", stdin->_fileno);
    printf("fileno of stdout = %d\n", stdout->_fileno);
    printf("fileno of stderr = %d\n", stderr->_fileno);
    
    return 0;
}

 

11.2 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);
功能:打开一个文件
参数1:文件路径,跟fopen的第一个参数一致
参数2:打开方式
        O_RDONLY :只读
        O_WRONLY:只写
        O_RDWR:读写
        ====================以上三个必须选择其中一个,下面的可以作为选加===================
        O_CREAT:如果文件不存在,创建一个文件,如果加了该选项,那么第三个参数也必须要加
        O_APPEND:追加写
        O_TRUNC:如果文件存在,则清空内容
        O_EXCL:判断文件是否已经存在,如果已经存在了,则open函数会置位错误码为:EEXIST,跟O_CREAT
            例如:"w+" : O_RDWR | O_CREAT | O_TRUNC                //多个值通过位或连接
        
参数3:文件权限,创建文件时的权限
            目录的最大权限:0777
            普通文件的最大权限是:0666
    最终的权限是给定的(mode & ~umask)           //0664 & (~umask) ->0664
    umask:是系统给定的默认的掩码
    更改掩码的指令:umask 数字
    
返回值:成功返回打开文件的文件描述符,失败返回-1,并置位错误码 

#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
    int fd;            //定义一个文件描述符

    umask(0002);         //将掩码设置成0

    //以读写的模式打开文件
    if((fd=open("./open.txt", O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1)
    {
        perror("open file");
        return -1;
    }

    //关闭文件
    close(fd);


    return 0;
}

 使用O_EXCL创建文件

#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<errno.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
    int fd;            //文件描述符

    //打开文件
    if((fd = open("./open.txt", O_RDWR|O_CREAT|O_EXCL)) == -1)
    {
        //判断错误原因
        if(errno == EEXIST)
        {
            printf("文件已存在,无需重新创建,直接打开即可。。。\n");
            fd = open("./open.txt", O_RDWR);
        }else
        {
            perror("open file");
            return -1;
        }
    }


    
    return 0;
}

11.3 umask

1> 创建文件时的掩码,默认问0002,表示其他用户对该文件没有写功能

2> 终端上查看掩码:umask

3> 更改掩码:

终端更改:umask 数字

代码更改:umask(数值);

11.4 close的使用

#include<myhead.h>

int main(int argc, const char *argv[])
{
    int fd;            //文件描述符

    //打开文件
    if((fd = open("./open.txt", O_RDWR|O_CREAT|O_EXCL)) == -1)
    {
        //判断错误原因
        if(errno == EEXIST)
        {
            printf("文件已存在,无需重新创建,直接打开即可。。。\n");
            fd = open("./open.txt", O_RDWR);
        }else
        {
            perror("open file");
            return -1;
        }
    }

    //关闭文件
    if(close(fd) == 0)
    {
        printf("关闭成功\n");
    }else
    {
        printf("关闭失败\n");
    }

    close(0);         //关闭标准输入文件
    close(1);
    close(2);





    return 0;
}

11.5 read函数的使用

       #include <unistd.h>

       ssize_t read(int fd, void *buf, size_t count);
功能:从给定文件中读取count个数据到buf中
参数1:被读取的文件
参数2:读取后数据存放的容器指针,是一个万能指针,也就是可以读取任意类型的数据
参数3:读取的个数
返回值:
        >0:读取字符的个数
        =0:说明文件读取结束
        <0:失败,并置位错误码

11.6 write函数的使用

       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);
功能:将buf中的数据写入到文件中,一共写count个字节
参数1:被读取的文件
参数2:读取后数据存放的容器指针,是一个万能指针,也就是可以读取任意类型的数据
参数3:读取的个数
返回值:成功返回写入的个数,如果啥也没写,就返回0
        失败返回-1,并置位错误码
#include<myhead.h>

int main(int argc, const char *argv[])
{
    //向标准出错中写数据
    write(2, "hello world\n", strlen("hello world\n"));

    //从标准输入中读取数据
    char buf[50] = "";
    read(0, buf, sizeof(buf));

    //printf("buf = %s\n", buf);
    //向标准输出中放数据
    write(1, buf, sizeof(buf));

    //定义文件描述符
    int fd;

    //以读写的形式打开文件
    if((fd = open("./open.txt", O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1)
    {
        perror("open file");
        return -1;
    }

    printf("fd = %d\n", fd);         //3

    //向文件中写入数据
    write(fd, buf, sizeof(buf));

    //关闭文件
    close(fd);


    return 0;
}

 

作业

使用read、write实现两个文件的拷贝

11.7 文件IO中的lseek函数

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

       off_t lseek(int fd, off_t offset, int whence);
功能:移动光标位置
参数1:文件描述符
参数2:偏移量
        >0:向后偏移
        <0:向前偏移
        =0:不偏移
参数3:偏移的起始位置
        SEEK_SET:文件开头
        SEEK_CUR:当前位置
        SEEK_END:文件结尾
返回值:
        成功返回光标当前位置到文件开头的偏移量
        失败返回(off_t)-1,并置位错误码
        lseek = fseek + ftell
    
#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件描述符
    int fd;

    //以只读的形式打开文件
    if((fd = open("./milaoshu.bmp", O_RDONLY)) == -1)
    {
        perror("open file");
        return -1;
    }

    //将光标偏移到文件结尾,返回值为从当前位置到文件开头的字节数
    printf("size of file = %ld\n", lseek(fd, 0, SEEK_END));      //输出的就是文件大小

    //关闭文件
    close(fd);

    return 0;
}

 

十二、文件描述符拷贝问题

12.1 文件描述符的直接拷贝

使用变量拷贝文件描述符,其实只是拷贝变量的值,并没有产生新的文件描述符,只是在程序中多了一个变量,记录该文件描述符而已,所以无论操作哪个变量,都是对该文件描述符的操作,使用的是同一个光标

#include<myhead.h>

int main(int argc, const char *argv[])
{
    int fd1;

    //以只读的形式打开一个文件
    if((fd1 = open("./open.txt", O_RDONLY)) == -1)
    {
        perror("open file");
        return -1;
    }

    //定义容器
    char buf[10] = "";

    int ret = read(fd1, buf, sizeof(buf));

    write(1, buf, ret);            //向终端打印数据
    printf("\n");

    int fd2 = fd1;         //将文件描述符直接拷贝

    printf("fd1 = %d, fd2 = %d\n", fd1, fd2);
    char buf1[10];
    ret = read(fd2, buf1, sizeof(buf1));
    write(1, buf1, ret);           //如果终端上输出的跟fd1输出结果一致
                                    //说明两个文件使用的是不同光标
                                    //如果不一致,说明两个文件使用的是同一光标



    return 0;
}

12.2 文件描述符拷贝之dup函数

       #include <unistd.h>

       int dup(int oldfd);
功能:通过给定的旧文件描述符,拷贝出一个新文件描述符
参数:旧文件描述符
返回值:成功返回:新文件描述符,使用最小未使用原则
        失败返回-1并置位错误码
#include<myhead.h>

int main(int argc, const char *argv[])
{
    int fd1,fd2;

    //以只读的形式打开文件
    if((fd1 = open("./open.txt", O_RDONLY)) == -1)
    {
        perror("open file");
        return -1;
    }

    //读取数据
    char buf1[10];
     int ret = read(fd1, buf1, sizeof(buf1));   //从文件中读取数据
    write(1, buf1, ret);                 //将数据打印在终端上

    //使用dup函数拷贝一个文件描述符
    fd2 = dup(fd1);

    printf("fd1 = %d, fd2 = %d\n", fd1, fd2);

    //通过fd2读取数据
    char buf2[10];
    ret =  read(fd2, buf2, sizeof(buf2)); 
    write(1, buf2, ret);   

    //关闭文件
    if(close(fd1))
    {
        perror("close fd1");
    }

    ret =  read(fd2, buf2, sizeof(buf2)); 
    write(1, buf2, ret);   

    if(close(fd2))
    {
        perror("close fd2");
    }

    return 0;
}

 作业:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF_SIZE 1024
int main(int argc, char *argv[])
{    
    int src_fd, dest_fd, rd_size, wt_size;    
    char buf[BUF_SIZE];    
    if (argc != 3) 
    {        
        fprintf(stderr, "Usage: %s <src_file> <dest_file>\n", argv[0]);                                        
         exit(EXIT_FAILURE);   
     }   
      src_fd = open(argv[1], O_RDONLY);   
     if (src_fd == -1)
     {       
     perror("open");       
     exit(EXIT_FAILURE);   
     }   
     dest_fd = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644);   
     if (dest_fd == -1) 
    {        
    perror("open");       
     exit(EXIT_FAILURE);   
     }    
     while ((rd_size = read(src_fd, buf, BUF_SIZE)) > 0) 
    {        
        wt_size = write(dest_fd, buf, rd_size);        
        if (wt_size != rd_size) 
        {            
            perror("write");           
             exit(EXIT_FAILURE);        
                }    }   
         if (rd_size == -1) 
        {        perror("read");        exit(EXIT_FAILURE);    }  
          if (close(src_fd) == -1) 
            {        perror("close");       
         exit(EXIT_FAILURE);    }    
        if (close(dest_fd) == -1) 
        {        
        perror("close");        
        exit(EXIT_FAILURE);   
         }   
         return EXIT_SUCCESS;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值