嵌入式学习 19(Linux高级编程——文件——文件IO)

标准IO之文件定位:

fseek()------在文件中移动文件指针的位置

函数原型:int fseek(FILE *stream, long offset, int whence);

功能:将stream流文件中的文件指针从whence位置开始偏移offset字节的长度。

参数:

stream :要移动文件指针的目标文件流对象。注意:不支持设备文件,一般用于普通文件。

offset :要在文件内偏移的距离,单位字节。

如果值为整数,则向文件末尾偏移

如果值为负数,则向文件开头偏移

whence :偏移的起始位置,由系统定义的三个宏开始。

SEEK_SET 文件的开头位置

SEEK_CUR 文件的当前位置

SEEK_END 文件的末尾位置

返回值:

成功: 返回 0

失败: -1;

如果从文件的指定位置向后偏移过程中已经超过了文件的当前末尾位置,则会自动以'\0'来填充文件内容,从而形成一种被称为"空洞文件" 的特殊文件。

使用 fseek() 函数可以灵活地在文件中定位,方便进行文件的随机读写操作。

cpp 复制代码
#include <stdio.h>

// 主函数,程序的入口点
int main(int argc, char *argv[])
{
    // 尝试以只读方式打开当前目录下的 "01fopen.c" 文件,并将文件指针存储在 fp 中
    FILE* fp = fopen("./01fopen.c","r");
    // 如果打开文件失败,fp 为 NULL
    if(NULL == fp)
    {
        // 输出提示信息,表示打开文件出错
        printf("fopen error\n");
        // 程序以错误状态码 1 退出
        return 1;
    }

    // 尝试将文件指针 fp 偏移 10 个字节,起始位置为文件开头(SEEK_SET)
    int ret = fseek(fp,10,SEEK_SET);
    // 如果偏移操作失败,ret 为 -1
    if(-1 == ret)
    {
        // 输出提示信息,表示文件偏移出错
        printf("fseek error\n");
        // 程序以错误状态码 1 退出
        return 1;
    }

    // 定义一个 512 字节大小的字符数组 buf,并初始化为全 0
    char buf[512]={0};
    // 从文件中读取一行内容到 buf 中,最多读取 sizeof(buf) - 1 个字符
    fgets(buf,sizeof(buf),fp);
    // 输出 buf 的内容
    printf("buf is %s\n",buf);

    // 关闭打开的文件
    fclose(fp);

    // 程序正常结束,返回 0
    return 0;
}

ftell()------用于获取当前文件指针相对于文件开头的偏移量

函数原型:long ftell(FILE *stream);

功能:获取当前文件流指针的具体位置,一般以文件开头到当前指针的字节数为返回值。

参数:stream 要返回指针距离的文件流对象

返回值:成功 获取到的距离长度,单位是字节失败 -1;

通过 ftell() 函数获取的偏移量通常以字节为单位,它在处理文件操作时非常有用,例如确定文件的大小记录读取或写入的位置等。

cpp 复制代码
#include <stdio.h>

// 主函数,程序的入口点
int main(int argc, char *argv[])
{
    // 尝试以只读方式打开当前目录下的 "2.png" 文件,并将文件指针存储在 fp 中
    FILE* fp = fopen("./2.png","r");
    // 如果打开文件失败,fp 为 NULL,程序直接返回 1 结束
    if(NULL == fp)
    {
        return 1;
    }

    // 将文件指针移到文件末尾
    fseek(fp,0,SEEK_END);
    // 获取当前文件指针的位置,即文件的大小,并将其存储在 size 变量中
    long size = ftell(fp);
    // 关闭打开的文件
    fclose(fp);
    // 输出文件的大小
    printf("size is %ld\n",size);

    // 程序正常结束,返回 0
    return 0;
}

rewind()------将文件指针重置到文件的开头

rewind() 等效于:fseek(stream,0L,SEEK_SET);

函数原型:void rewind(FILE *stream);

使用 rewind() 函数可以方便地重新从文件开头进行读取或其他操作,无需再次调用 fopen() 打开文件。

cpp 复制代码
#include <stdio.h>

// 主函数,程序的入口点
int main(int argc, char *argv[])
{
    // 尝试以只读方式打开当前目录下的 "01fopen.c" 文件,并将文件指针存储在 fp 中
    FILE* fp = fopen("./01fopen.c","r");
    // 如果打开文件失败,fp 为 NULL,程序直接返回 1 结束
    if(NULL == fp)
    {
        return 1;
    }

    // 将文件指针移到文件末尾
    fseek(fp, 0, SEEK_END);
    // 获取当前文件指针的位置,即文件的大小,并将其存储在 size 变量中
    long size = ftell(fp);
    // 输出文件的大小
    printf("size is %ld\n", size);
    // 将文件指针重置到文件开头
    rewind(fp);
    // 定义一个 512 字节大小的字符数组 buf,并初始化为全 0
    char buf[512] = {0};
    // 尝试从文件中读取一行内容到 buf 中
    if (fgets(buf, sizeof(buf), fp))
    {
        // 如果读取成功,输出 buf 的内容
        printf("buf is %s\n", buf);  
    }
    else 
    {
        // 如果读取失败,输出提示信息,表示已到达文件末尾
        printf("end of file\n");
    }
    // 关闭打开的文件
    fclose(fp);

    // 程序正常结束,返回 0
    return 0;
}

缓冲类型

1. 行缓冲 (主要用于人机交互的 stdout,如终端):缓冲区大小通常为 1K(1024 字节)。
刷新缓冲区 的情况有以下几种:

遇到换行符 \n 时刷新。

缓冲区满了就刷新。

程序结束时刷新。

使用 fflush(stdout) 强制刷新。

行缓冲多用于终端相关的操作,比如在终端中输入和输出信息。

cpp 复制代码
#include <stdio.h>

int main() 
{
    printf("这是第一行,没有换行\n");
    printf("这是第二行,有换行\n");
    fflush(stdout);  // 强制刷新缓冲区

    return 0;
}

第一行输出由于没有换行符,可能不会立即显示,直到缓冲区满或遇到后面的强制刷新。第二行由于有换行符会立即显示。

2. 全缓冲 (主要用于文件的读写):缓冲区大小一般为 4K(4096 字节)。

对于普通文件的标准 I/O 操作,通常建立的缓存是全缓冲。

刷新缓冲区的条件为:

缓冲区满时刷新。

程序结束时刷新。

使用 fflush(fp) 强制刷新。

cpp 复制代码
#include <stdio.h>

int main() 
{
    FILE *fp = fopen("output.txt", "w");
    if (fp == NULL) 
    {
        printf("文件打开失败\n");
        return 1;
    }

    for (int i = 0; i < 3000; i++) 
    {
        fprintf(fp, "%d ", i);
    }
    fflush(fp);  // 强制刷新缓冲区

    fclose(fp);

    return 0;
}

向文件写入数据,直到写入的数据接近 4096 字节或进行强制刷新,数据才会真正写入文件。

3. 无缓冲 (主要用于出错处理信息的输出 stderr):

缓冲区大小为 0K,意味着不对数据进行缓存,直接刷新。

常用于输出出错处理相关的信息,比如 fprintf(stderr, "fopen error %s", filename); 。

cpp 复制代码
#include <stdio.h>

int main() 
{
    fprintf(stderr, "这是错误信息,无缓冲,立即显示\n");
    return 0;
}

由于 stderr 是无缓冲的,输出的错误信息会立即显示。

文件IO

open()------系统调用函数,用于打开文件

int open(const char *pathname, int flags); 中,pathname 是要打开文件的路径和文件名,flags 用于指定打开文件的模式,例如 O_RDONLY 表示只读打开,O_WRONLY 表示只写打开,O_RDWR 表示读写打开等。
int open(const char *pathname, int flags, mode_t mode); 则在前者的基础上增加了 mode 参数。当以创建新文件的方式打开(如使用 O_CREAT 标志)时,mode 用于指定新文件的权限。

返回值: 成功返回文件描述符,失败返回-1;

打开一个指定的文件,如果打开成功,输出文件描述符,否则报告错误并退出:

cpp 复制代码
#include <sys/types.h>  // 包含系统数据类型定义的头文件
#include <sys/stat.h>   // 包含文件状态相关的结构和宏定义的头文件
#include <fcntl.h>      // 包含文件控制选项相关的定义和函数声明的头文件
#include <stdio.h>      // 包含标准输入输出相关的函数声明的头文件

int main(int argc, const char *argv[])  // 主函数,argc 表示命令行参数的个数,argv 是参数数组
{
    int fd = open("1.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);  // 尝试以只写、创建新文件(若不存在)、截断文件(若存在)的方式打开文件 "1.txt",并指定文件权限为 0666
    if(fd == -1)  // 如果打开文件失败(返回值为 -1)
    {
        fprintf(stderr,"open error\n");  // 向标准错误输出打印"open error"
        return 1;  // 程序以错误状态 1 退出
    }

    printf("fd = %d\n",fd);  // 打印打开文件成功后返回的文件描述符的值
    return 0;  // 程序正常结束,返回 0
}

O_WRONLY 表示只写模式打开文件,O_CREAT 表示如果文件不存在则创建新文件,O_TRUNC 表示如果文件存在则截断为 0 长度。0666 是设置的文件权限,分别表示所有者、所属组和其他用户都有读写权限。

write()------系统调用函数,用于向文件或文件描述符写入数据

函数原型为:ssize_t write(int fd, const void *buf, size_t count);

fd 是文件描述符,指定要写入数据的目标文件。

buf 是指向要写入数据的缓冲区的指针。

count 是要写入的有效字节数。

函数返回实际写入的字节数,如果返回值为 -1 ,表示写入操作出错。

cpp 复制代码
#include <sys/types.h>  // 包含系统数据类型定义的头文件
#include <sys/stat.h>   // 包含文件状态相关的结构和宏定义的头文件
#include <fcntl.h>      // 包含文件控制选项相关的定义和函数声明的头文件
#include <stdio.h>      // 包含标准输入输出相关的函数声明的头文件
#include <unistd.h>     // 包含一些系统调用函数,如 write 和 close
#include <string.h>     // 包含字符串操作相关的函数,如 strlen

int main(int argc, const char *argv[])  // 主函数,接收命令行参数
{
    int fd = open("1.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);  // 尝试打开或创建并截断文件 "1.txt",设置权限为 0666,并获取文件描述符
    if(fd == -1)  // 如果打开操作失败(文件描述符为 -1)
    {
        fprintf(stderr,"open error\n");  // 向标准错误输出打印"open error"
        return 1;  // 以错误状态 1 退出程序
    }

    printf("fd = %d\n",fd);  // 打印成功打开文件后的文件描述符

    char buf[512] = "hello";  // 定义一个字符数组作为要写入文件的数据
    int ret = write(fd,buf,strlen(buf));  // 向文件写入数据,返回实际写入的字节数
    if(ret == -1)  // 如果写入操作失败(返回值为 -1)
    {
        fprintf(stderr,"write error\n");  // 向标准错误输出打印"write error"
        return 1;  // 以错误状态 1 退出程序
    }

    close(fd);  // 关闭文件

    return 0;  // 程序正常结束,返回 0
}

read()------系统调用函数,用于从文件描述符读取数据

函数原型为:

cpp 复制代码
ssize_t read(int fd, void *buf, size_t count);

fd:文件描述符,指定要从哪个文件读取数据。

buf:用于存储读取数据的缓冲区。

count:指定要读取的最大字节数。

函数返回值:

成功读取的字节数,如果到达文件末尾则返回 0 。

如果发生错误,返回 -1 ,并设置 errno 来指示错误类型。

例如,如果成功从文件描述符 fd 读取了 50 个字节的数据到 buf 中,read() 函数就会返回 50 。

cpp 复制代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    // 尝试以只读方式打开文件 "1.txt",并获取文件描述符
    int fd = open("1.txt", O_RDONLY);    
    // 如果打开文件失败,文件描述符为 -1
    if(-1 == fd)
    {
        // 向标准错误输出打印 "open error"
        fprintf(stderr,"open error\n");
        // 程序返回 1,表示出现错误
        return 1;
    }
    // 打印打开成功的文件描述符
    printf("fd is %d\n",fd);

    // 定义一个 512 字节大小的字符数组 buf 用于存储读取的数据,并初始化为 0
    char buf[512]={0};

    // 进入一个无限循环,持续读取文件数据
    while(1)
    {
        // 调用 read 函数从文件描述符 fd 读取数据到 buf 中,读取 buf 大小的数据
        int ret = read(fd, buf, sizeof(buf));
        // 如果读取到的数据字节数小于等于 0,表示已读完文件或读取出错
        if(ret<=0)
        {
            // 退出循环
            break;
        }
        // 将读取到的数据以字符串形式打印到标准输出
        printf("%s\n", buf);
    }

    // 关闭文件描述符
    close(fd);
    // 程序正常返回 0
    return 0;
}

实现文件的复制:如./a.out 1.txt 2.txt(类似cp 1.txt 2.txt)

cpp 复制代码
#include <sys/types.h>  // 包含系统数据类型定义的头文件
#include <sys/stat.h>   // 包含文件状态相关的结构和宏定义的头文件
#include <fcntl.h>      // 包含文件控制选项相关的定义和函数声明的头文件
#include <stdio.h>      // 包含标准输入输出相关的函数声明的头文件
#include <unistd.h>     // 包含一些系统调用函数,如 read、write 和 close

// 该程序实现了一个简单的文件复制功能
int main(int argc, const char *argv[])  // 主函数,接收命令行参数
{
    if(argc < 3)  // 如果命令行参数个数小于 3(程序名、源文件名、目标文件名)
    {
        fprintf(stderr,"usage:./a.out srcfile dstfile\n");  // 向标准错误输出提示用法
        return 1;  // 以错误状态 1 退出程序
    }

    int src_fd = open(argv[1], O_RDONLY);  // 以只读方式打开源文件,并获取文件描述符
    int dest_fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0666);  // 以只写、创建新文件(若不存在)、截断文件(若存在)的方式打开目标文件,并指定权限为 0666,获取文件描述符

    if(src_fd == -1 && dest_fd == -1)  // 如果两个文件打开都失败(文件描述符为 -1)
    {
        fprintf(stderr,"open error\n");  // 向标准错误输出打印"open error"
        return 1;  // 以错误状态 1 退出程序
    }

    while(1)  // 无限循环,直到读取文件结束
    {
        char buf[512] = {0};  // 定义一个 512 字节的缓冲区,并初始化为 0
        int rd_ret = read(src_fd, buf, sizeof(buf));  // 从源文件读取数据到缓冲区,获取读取的字节数
        if(rd_ret <= 0 )  // 如果读取的字节数小于等于 0,表示读取结束或出错
        {
            break;  // 退出循环
        }
        write(dest_fd, buf, rd_ret);  // 将读取到的数据写入目标文件
    }

    close(src_fd);  // 关闭源文件
    close(dest_fd);  // 关闭目标文件

    return 0;  // 程序正常结束,返回 0
}

lseek()------用于设置文件描述符的偏移量,也就是指定文件的读写位置

函数原型:

cpp 复制代码
off_t lseek(int fd, off_t offset, int whence);

fd:文件描述符。

offset:偏移量,表示要移动的字节数。

whence:指定偏移量的基准位置,有以下几种取值:

◦ SEEK_SET:从文件开头开始计算偏移量。

◦ SEEK_CUR:从当前位置开始计算偏移量。

◦ SEEK_END:从文件末尾开始计算偏移量。

函数返回值:

成功时返回新的偏移量位置(以字节为单位)。

失败时返回 -1,并设置 errno 来指示错误类型。

cpp 复制代码
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <string.h>

int main(int argc, char *argv[]) 
{     
    // 以读写方式打开文件 "1.txt",并获取文件描述符
    int fd = open("1.txt", O_RDWR);         
    // 如果打开文件失败(文件描述符为 -1)
    if(-1 == fd)     
    {         
        // 向标准错误输出打印 "open error"
        fprintf(stderr,"open error\n");         
        // 程序返回 1,表示出现错误
        return 1;     
    }     
    // 使用 lseek 函数将文件读写位置设置到距文件开头 15 个字节处
    off_t off = lseek(fd, 15, SEEK_SET);     
    // 打印新设置的偏移量
    printf("off %ld\n", off);     
    // 定义一个字符数组 buf 并初始化为 "hello"
    char buf[]="hello";     
    // 将 "hello" 写入文件
    write(fd, buf, strlen(buf)); 
    // 打印文件描述符
    printf("fd is %d\n", fd);     
    // 关闭文件描述符
    close(fd);     
    // 程序正常返回 0
    return 0; 
}
相关推荐
南宫理的日知录5 分钟前
99、Python并发编程:多线程的问题、临界资源以及同步机制
开发语言·python·学习·编程学习
xinghuitunan22 分钟前
蓝桥杯顺子日期(填空题)
c语言·蓝桥杯
Half-up24 分钟前
C语言心型代码解析
c语言·开发语言
数据与后端架构提升之路1 小时前
从神经元到神经网络:深度学习的进化之旅
人工智能·神经网络·学习
懒大王就是我1 小时前
C语言网络编程 -- TCP/iP协议
c语言·网络·tcp/ip
半盏茶香1 小时前
【C语言】分支和循环详解(下)猜数字游戏
c语言·开发语言·c++·算法·游戏
一行11 小时前
电脑蓝屏debug学习
学习·电脑
小堇不是码农1 小时前
在VScode中配置C_C++环境
c语言·c++·vscode
小肥象不是小飞象1 小时前
(六千字心得笔记)零基础C语言入门第八课——函数(上)
c语言·开发语言·笔记·1024程序员节
星LZX1 小时前
WireShark入门学习笔记
笔记·学习·wireshark