Linux文件基本操作

Linux 的设计哲学

在 Linux 中,一切皆文件!

什么是文件?

文件是具有永久存储性,按特定字节顺序组成的命名数据集

文件可分为:文本文件,二进制文件

文本文件:每个文件存放一个 ASCII 码

  • 存储量大,速度慢,便于对字符操作

二进制文件:数据按照在内存中的存储形式原样存放

  • 存储量小,速度快,便于存放中间结果

Linux 文件编程

在 Linux 中,除了常规文件,目录,设备,管道等,也属于文件

ASCII C 文件编程

标准 C 文件接口建立于 Linux 原生文件接口之上,使用缓冲区机制提高效率

缓冲区是一片特殊的内存空间,用于暂存文件中的数据

  • 读:一次性将大量的数据读入缓冲区 (后续再从缓冲区中拿数据)
  • 写:可先把数据写入输入缓冲区 (缓冲区满之后再把数据一次性写入文件)
  • 缓冲区的引入是为了避免频繁的磁盘操作,提高文件读写的整体效率

深入 ASCII C 文件编程

由于引入了缓冲区,ASCII C 文件编程是一种基于数据流的编程

ASCII C 文件编程接口

ASCII C 文件打开模式

文本文件写示例

ASCII C 文件 "读写移动指针"

int fseek(FILE* stream, long offset, int whence);

  • 移动文件读写指针,whence => SEEK_SET,SEEK_END,SEEK_CUR

long ftell(FILE* stream);

  • 获取当前读写指针的位置 (对于文件起始位置)

void rewind(FILE* stream);

  • 将读写指针置于文件起始位置,(void)fseek(stream, 0L, SEEK_SET)

二进制文件读写示例

ASCII C 文件编程初体验

test1.c

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>

int main() 
{
    FILE* fp = NULL;
    char student[50] = {0}; 
    int i = 0;
    
    if( (fp = fopen("input.txt", "w")) == NULL )
    {
        perror("open file error...\n");
        exit(1);
    }
    
    for(i=0; i<3; i++)
    {
        printf("input name:");
        scanf("%s", student);
        fputs(student, fp);
        fputs("\n", fp);
    }
    
    fclose(fp);
    
    return 0;
}

第 13 行,我们以写的方式打开一个文本文件

第 23 行,通过 fputs(...) 将字符串写入到文本文件中

程序运行结果如下图所示:

test2.c

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

#define N   3

struct student
{
  long num;
  char name[16];
  int age;
};

int main()
{
    int i = 0;
    struct student s = {0};
    FILE* fp = NULL;

    if( (fp = fopen("student.dat", "wb+")) == NULL )
    {
        perror("open file error...\n");
        exit(1);
    }

    for(i=0; i<N; i++)
    {
        printf("Number: %d\n", i + 1);
        printf("ID:");
        scanf("%ld", &s.num);
        printf("Name:");
        scanf("%s", s.name);
        printf("Age:");
        scanf("%d", &s.age);
        printf("\n");
        
        fwrite(&s, sizeof(s), 1, fp);
    }

    rewind(fp);
      
    for(i=0; i<N; i++)
    {
        fread(&s, sizeof(s), 1, fp);
        
        printf("%ld %s %d\n", s.num, s.name, s.age);
    }

    fclose(fp);
    
    return 0;
}

第 19 行,我们以写入并新建文件的方式打开一个二进制文件

第 25 - 37 行,我们通过 scanf 来填充结构体的内容,并通过 fwrite 写入到二进制文件中

由于文件读写指针会在我们进行文件写入操作后,偏移位置在文件的末尾,所以在接下来我们需要读取文件内容的时候,可以使用 rewind(...) 函数,将文件读写指针偏移到文件的起始位置处,再进行读取

程序运行结果如下图所示:

ASCII 文件缓冲区类型

全缓冲区:默认缓冲器大小为 BUFSIZ,具体大小与系统相关

  • 缓冲区满 或 调用 fflush() 才通过系统调用将数据写入磁盘 (设备)

行缓冲区:默认缓冲区大小为 128 字节,具体大小与系统有关

  • 遇见 换行符 或 缓冲区满 或 调用 fflush() 后通过系统调用将数据写入磁盘 (设备)

无缓冲区:不对数据进行缓冲

  • 相当于直接使用系统调用 write(),数据立即写入磁盘 (设备)

自定义文件缓冲区

缓冲区代码示例

文件缓冲区编程实验

test3.c

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>

int main() 
{
    FILE* fp = NULL;
    char buf[128] = {0}; 
    char* ps = "Delphi\nTang";
    
    if( (fp = fopen("input.txt", "w")) == NULL )
    {
        perror("open file error...\n");
        exit(1);
    }
    
    setvbuf(fp, buf, _IOLBF, sizeof(buf));
    
    fwrite(ps, sizeof(*ps), strlen(ps), fp);
    
    printf("ps = %s\n", buf);
    
    fclose(fp);
    
    return 0;
}

第 19 行,我们通过 setvbuf(...) 函数将 input.txt 这个文件的缓冲类型设置为行缓冲,缓冲区为我们定义的 buf,缓冲区大小为 128 字节

第 21 行,我们通过 fwrite(...) 将字符串写入到文件中,由于我们设置了行缓冲,数据会先写入到缓冲区中,当遇到换行符后,会将缓冲区的 "Delphi" 字符串写入到文件中,字符串 "Tang" 还暂存在缓冲区中

第 25 行,我们调用了 fclose(...),把缓冲区中的字符串 "Tang" 也写入到文件中

程序运行结果如下图所示:

缓冲区的内容为 "Tanghi",是因为 "hi" 为脏数据,是将 "Delphi" 写入到缓冲区时保留下来的

test4.c

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


struct student
{
    long num;
    char name[16];
    int age;
};

int main(int argc, char* argv[]) 
{
    FILE* fp = NULL;
    char buf[BUFSIZ] = {0};
    struct student s = {999, "tang", 888};
    struct student* ps = NULL;
    
    if( (fp = fopen("input.txt", "w")) == NULL )
    {
        printf("open file error: %d...\n", __LINE__);
        exit(1);
    }
    
    printf("BUFSIZ = %d\n", BUFSIZ);
    
    setvbuf(fp, buf, _IOFBF, sizeof(buf));
    
    fwrite(&s, sizeof(s), 1, fp);
    
    fclose(fp);
    
    ps = (void*)buf;
    
    printf("num = %ld,  name = %s,  age = %d\n", ps->num, ps->name, ps->age);
    
    /
    
    if( (fp = fopen("input.txt", "r")) == NULL )
    {
        printf("open file error: %d...\n", __LINE__);
        exit(2);
    }
    
    setvbuf(fp, buf, _IOFBF, sizeof(buf));
    
    memset(&s, 0, sizeof(s));
    memset(buf, 0, sizeof(buf));
    
    fread(&s, sizeof(s), 1, fp);
    
    printf("num = %ld,  name = %s,  age = %d\n", s.num, s.name, s.age);
    
    fclose(fp);
    
    ps = (void*)buf;
    
    printf("num = %ld,  name = %s,  age = %d\n", ps->num, ps->name, ps->age);
    
    return 0;
}

第 31 行,打印系统预定义的文件默认缓冲区大小的宏

第 33 行,设置文件缓冲区为全缓冲

第 35 行,我们将结构体 s 的内容写入到文件中,由于设置的是全缓冲,写入的结构体大小没有超过缓冲区大小,所以会先将数据暂存在缓冲区 buf 中

第 37 行,关闭文件,会将缓冲区暂存的数据写入到文件中去

程序运行结果如下图所示:

相关推荐
小糖学代码8 小时前
LLM系列:1.python入门:3.布尔型对象
linux·开发语言·python
shizhan_cloud8 小时前
Shell 函数的知识与实践
linux·运维
Deng8723473488 小时前
代码语法检查工具
linux·服务器·windows
霍夫曼10 小时前
UTC时间与本地时间转换问题
java·linux·服务器·前端·javascript
月熊11 小时前
在root无法通过登录界面进去时,通过原本的普通用户qiujian如何把它修改为自己指定的用户名
linux·运维·服务器
大江东去浪淘尽千古风流人物12 小时前
【DSP】向量化操作的误差来源分析及其经典解决方案
linux·运维·人工智能·算法·vr·dsp开发·mr
赖small强13 小时前
【Linux驱动开发】NOR Flash 技术原理与 Linux 系统应用全解析
linux·驱动开发·nor flash·芯片内执行
IT运维爱好者14 小时前
【Linux】LVM理论介绍、实战操作
linux·磁盘扩容·lvm
LEEE@FPGA14 小时前
ZYNQ MPSOC linux hello world
linux·运维·服务器
郝学胜-神的一滴14 小时前
Linux定时器编程:深入理解setitimer函数
linux·服务器·开发语言·c++·程序人生