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 行,关闭文件,会将缓冲区暂存的数据写入到文件中去

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

相关推荐
轻松Ai享生活几秒前
一步步学习Linux initrd/initramfs
linux
轻松Ai享生活4 分钟前
一步步深入学习Linux Process Scheduling
linux
绵绵细雨中的乡音2 小时前
网络基础知识
linux·网络
Peter·Pan爱编程2 小时前
Docker在Linux中安装与使用教程
linux·docker·eureka
kunge20133 小时前
Ubuntu22.04 安装virtualbox7.1
linux·virtualbox
清溪5493 小时前
DVWA中级
linux
Sadsvit4 小时前
源码编译安装LAMP架构并部署WordPress(CentOS 7)
linux·运维·服务器·架构·centos
xiaok4 小时前
为什么 lsof 显示多个 nginx 都在 “使用 443”?
linux
苦学编程的谢4 小时前
Linux
linux·运维·服务器
G_H_S_3_5 小时前
【网络运维】Linux 文本处理利器:sed 命令
linux·运维·网络·操作文本