c语言中的文件操作

article/2025/10/11 23:17:46

目录

引入:

一、文件的打开和关闭

 1.文件名

 2.文件指针

3.文件的打开和关闭

二、对文件如何操作和流

1.对文件操作相关功能: 

2.流

三、文件操作函数(顺序读写)

 1."w"输出流:

2."r" 输入流

3."wb"与"rb"二进制输出输入

4.sscanf与sprintf函数

四、fseek相关函数介绍(随机读写) 

1.fseek函数 

2.ftell

3.rewind

五、文件缓冲区 


引入:

磁盘上的文件都是文件。

对于日常生活中,我们在使用电子设备的时候,用到某个软件或者应用的时候,总是发现他们会在本地占用空间,即文件,是不是打开软件后,先前保留的数据都不见了呢?不是的,这些数据是被存放在文件里面的。

之前我们制作的动态储存版的通讯录(一个小项目,数据只能储存到内存中),程序是不是只要一关闭,保存的数据下次打开就消失了。这是因为没有将其数据保存在文件中,而是内存中,结束程序内存的空间也自然被释放,从而无法保留。

那么如果想要改进这个通讯录自然要加上文件操作的函数让其数据保留在我们的硬盘上:

而在程序设计中:文件一般包括两种,一种就是我们写程序所得到的文件(程序文件,比如:.cc语言源文件 .obj(windows环境下)目标文件,.exe可执行程序)第二种就是通过程序来操作输入输出的文件(数据文件:从某个文件通过程序写入或者拿取数据的文件)。

所以,接下来我们一起来了解文件操作的相关函数的知识吧!

一、文件的打开和关闭

 1.文件名

 首先了解一下文件名:

文件名 = 文件路径 + 文件名主干 + 文件后缀。在Windows下要注意的是:

(1)文件名最长可以使用255个字符。

(2)可以使用扩展名,扩展名用来表示文件类型,也可以使用多间隔符的扩展名。如win.ini.txt是一个合法的文件名,但其文件类型由最后一个扩展名决定。

(3)文件名中允许使用空格,但不允许使用下列字符(英文输入法状态):< > / \ | : " * ?

(4)windows系统对文件名中字母的大小写在显示时有不同,但在使用时不区分大小写。

 (上面参考百度百科)

那么,当文件被创建或者使用时,电脑又是如何感知到它的存在的呢?

 2.文件指针

那么这里就要涉及到指针相关的知识了。

要操作一个文件,被使用的文件(无论读或者写)就会创建一个文件信息区(结构体对象,FILE类型,在内存里面创建,强关联,用来描述文件基本信息的

文件基本信息可以参考一下截图:

 就是类型,位置,大小等等信息了。所以每次操作文件的时候,内存内总会出现文件信息区与之关联,好让我们操作。

通常,在c中,将文件指针变量如此定义:

FILE* pr //文件指针变量,通过该指针便就可以操作相关文件。

3.文件的打开和关闭

现在重头戏来啦:我们已经了解了文件指针,那么要如何在程序中通过该指针对文件进行打开和关闭操作呢?

(1).打开(open) :

FILE* pr = fopen("C:\\Users\\HP\\Desktop HELLO WORLD", "r");

上面的操作就是对我们之前创建的文件进行打开,并且*只读操作

 *部分不懂先不急,后续会补充。

该函数fopen即为文件打开函数:FILE* fopen(const char* name, const char* mod);返回文件信息区的地址,第一个是文件名,第二个是打开操作,如果打开失败,返回NULL。

(2).关闭(close

fclose(pr);

上面的操作就是对刚刚打开的文件信息区进行关闭,即关闭文件函数。

fclose即为文件关闭函数,int fclose( FILE *stream );文件关闭。如果*流被成功关闭,fclose返回0。函数返回EOF表示错误。

一般关闭文件后,需要给文件指针给NULL,防止成为野指针。

看下面的打开与关闭文件代码:

    FILE* or = fopen("test.txt", "r");//只是文件名加扩展名的话只在源文件目录下进行寻找,如果打开失败,那么就返回NULL
    if (or == NULL) {
        perror("fopen");
        return 1;
    }
    fclose(or);//关闭此文件。关闭成功返回0,否则返回EOF即发生错误。
    or = NULL;
    return 0;

在了解了文件的打开与关闭后,我们就来了解一下对文件进行怎样操作的相关功能和流的介绍:

二、对文件如何操作和流

1.对文件操作相关功能: 

fopen第二个参数 (使用方式)                             含义                       

 "r"                  打开,为了只读 。如果打开文件不存在,报错。 
"w"                  为了只写 如果文件存在,会将原来内容销毁,然后在写,没有就会在文件名下开辟
"a"                  为了追加,不会将内容摧毁的条件下写。
"rb"                 二进制只读,如果打开文件不存在,报错。 
"wb"                二进制只写,文件存在,会将原来内容销毁,然后在写,没有就会在文件名下开辟
"wa"                二进制追加,不会将内容摧毁的条件下写。                                                                

目前只对上述操作进行介绍,并且在介绍其功能时,也一边将文件操作函数带入:

针对r和w的含义,特别的:

    //在w只写的条件下,对文件输出字符。如果找不到该文件,则会创建一个,如果找到了,则会销毁里面的内容。
    FILE* pr = fopen("test.txt", "w");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    int a = fputc('x', pr);
    fclose(pr);
    pr = NULL;
    printf("%c\n", a);
    return 0; 

请多加留意一下文件里的内容哦! 

2.流

流可以形容成:水流
外部设备封装个流
电脑上有各种的外部设备(键盘、屏幕、u盘、硬盘、网卡】.......)
把数据写入流,读取数据从流里面读。

  


流就有:文件流(文件指针)标准输入(stdin键盘)、输出(stdout屏幕)、错误流(stderr屏幕)(只要c程序运行起来,这三个流默认打开,类型也是文件指针 FILE*))

三、文件操作函数(顺序读写)

 1."w"输出流:

前提,"w"表明在打开文件的时候,是用的 FILE* pr = fopen("test.txt", "w");方式打开,以只写的方式,即输出,向流输出,即输出流。

 

 (1).fputc 字符操作  

函数原型:int fputc(int c, FILE* ar)c为要写入的字符变量,存入文件之中。返回的是写入的字符的ASCII码值,出现错误就返回EOF

    //使用字符存入利用循环对文件进行存入字符串
    FILE* pr = fopen("test.txt", "w");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    int a = 'A';
    for (int i = 0; i < 26; i++)
    {
        fputc(a++, pr); //往文件里面存入ABCDEFG........
    }
    fclose(pr);
    pr = NULL; 

(2).fputs 文本行(字符串)操作 

函数原型:int fputs(const char *string, FILE* ar);将字符串(传入的是该字符串或者文本首元素地址),写入文件如果成功,每个函数都会返回一个非负值。当发生错误时,fputs返回EOF 

    //直接用字符串存入函数即可;
    FILE* pr = fopen("test.txt", "w");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fputs("ABCDEF\n", pr);//改完后文件里面存ABCDEF
    fclose(pr);
    pr = NULL; 

(3).fprintf 针对于所有输出流的格式化输出函数

函数原型:int fprintf( FILE *stream, const char *format [, argument ]...);格式化输出,所有输出流,后面和printf一致,针对于w只写

因为具有格式化进行输出了,那么就要有对于的格式化操作符,下面例子使用结构体方便举例:

    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { "小刘", 'L', 13 };
    FILE* arr = fopen("test3.txt", "w");
    if (arr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fprintf(arr, "%s %c %d", c.name, c.xingbie, c.age);//上述代码会在源代码根目下创建一个新的test3文件,并且写入结构体内存入的数据。
    fclose(arr);
    arr = NULL;

其实就是和原来的只针对标准流输出的printf差了第一个文件指针而已,可以对比记忆哦! 

2."r" 输入流

前提,"r"表明在打开文件的时候,是用的 FILE* pr = fopen("test.txt", "r");方式打开,以只读的方式,即输入,从流输入程序,即输入流。

(1)fgetc 字符输入

 函数原型:int fgetc(FILE* ar);将文件中的字符输入程序中,返回的是读到的字符的ASCII码值 ,遇到错误或者结束就返回EOF。

    FILE* pr = fopen("E:\\学习资料\\C\\xuexi\\text_2022\\wenjiantest_4_6\\test2.txt", "r");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    //利用字符操作循环输入
    //printf("%c\n", fgetc(pr));
    //printf("%c\n", fgetc(pr));
    //printf("%c\n", fgetc(pr));
    int a = 0;
    while ((a = fgetc(pr)) != EOF)
    {
        printf("%c", a);
    }

     fclose(pr);
    arr = NULL;

上述是两种输入方法;另外:(fgetc(stdin)键盘里读)也是读取键盘里的字符。

(2).fgets 文本行输入

函数原型:char *fgets( char *string, int n, FILE *stream );三个参数分别是读文件所放位置,最大的个数(读多少个字符,最多读255个字符)读的时候实际上读n - 1个。最后一个要给\0  函数返回字符串,如果读完或者没有,返回NULL 

    char arr[255] = { 0 };
    fgets(arr, 255, pr);
    printf("%s\n", arr);

自己对比记忆也可哦!

(3).fscanf 针对于所有输入流的格式化输入函数

函数原型:int fscanf( FILE *stream, const char *format [, argument ]... );格式化输入,所有输入流,后面和scanf一致。针对于r只读 。

同理,也是用结构体来举例:

    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { 0 };
    FILE* pr = fopen("test3.txt", "r");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fscanf(pr, "%s %c %d", c.name, &(c.xingbie), &(c.age));
    printf("%s %c %d", c.name, c.xingbie, c.age);
    fclose(pr);
    pr = NULL;

记忆与printf相似,多了一个文件指针。

3."wb"与"rb"二进制输出输入

上面的wb和rb只与w和r差别一个b,表现了使用二进制交换的,和之前的文本不同:

FILE* pr = fopen("test.txt", "wb");//FILE* pr =  fopen("test.txt", "rb");

与上面文件打开方式对应的文件操作函数是:fwrite和fread

(1)fwrite (在"wb"条件下) 输出操作

函数原型size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
                                         指针指向要写的数据 写多大 最多写几个数据   写入哪个流(单位字节)fwrite返回实际写入的完整项数,如果发生错误,这个数可能小于count。此外,如果发生错误,则无法确定文件位置指示器。

    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { "张三", 'L', 19 };
    FILE* pr = fopen("test4.txt", "wb");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fwrite(&c, sizeof(struct student), 1, pr);

打开文件后,会发现有乱码,这是因为是用二进制写入的。

(2). fread(在"rb"条件下) 输入操作

函数原型:   size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
                                   指针指向要存的数据地址  读多大  最多读几个数据   取出哪个流(单位字节) 返回实际读到的元素 

    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };                                                                                                                                                struct student d = { 0 };
    FILE* pr = fopen("test4.txt", "rb");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fread(&d, sizeof(struct student), 2, pr);
    printf("%s %c %d", d.name, d.xingbie, d.age);
    fclose(pr);
    pr = NULL;

4.sscanf与sprintf函数

下面是两个函数的原型和解析:

                    sscanf - int sscanf( const char *buffer, const char *format [, argument ] ... );从一个字符串里面读取一个格式化的数据。后面和scanf一致,第一个要提取字符串的地址
                    sprintf - int sprintf( char *buffer, const char *format [, argument] ... );把格式化的数据转化为字符串,后面和printf一致,前面是储存字符串的地址。 

    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { 0 };
    sscanf("小明 N 14", "%s %c %d", c.name, &(c.xingbie), &(c.age));
    printf("%s %c %d", c.name, c.xingbie, c.age);
    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { "张三", 'L', 19 };
    char arr[256] = { 0 };
    sprintf(arr, "%s %c %d", c.name, c.xingbie, c.age);
    printf("%s\n", arr); 

四、fseek相关函数介绍(随机读写) 

1.fseek函数 

上面介绍的全是关于文件里面顺序读写的。

那么,我们能否自由指定位置呢?当然是可以实现的。

在上面代码实现fgetc函数的时候,你会发现读取一个然后就会读取下一个,不会重复读取的存在,而写自然也是一样,这说明了文件指针也是会随着读写发生变化的,证明了我们是能够操控这个位置的,下面我们首先向文件导入一串字符串:

int main()
{
    //文件的随机读写,我要往哪里去读写 文件里面一开始有记录读取文件的位置的,默认是起始位置。
    FILE* pr = fopen("test.txt", "w");//以只写的方式打开
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fputs("nihaoshijie", pr);//先写入进去;
    fclose(pr);
    pr = NULL;
    return 0;
}
 

存好之后,我们会发现文本文件里面存在

 

下面介绍一下fseek函数的使用:

函数原型:int fseek( FILE *stream, long offset, int origin );三个参数:origin:SEEK_CUR -文件当前 SEEK_END 末尾 SEEK_SET 开始  offset:偏移量 表现了通过起始坐标的不同,我们便就可以操作目前读取或者写入的位置了,下面以读取为例子:

int main()
{
    //文件的随机读写,我要往哪里去读写 文件里面一开始有记录读取文件的位置的,默认是起始位置。
    FILE* pr = fopen("test.txt", "r");//以只读的方式打开
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    printf("%c", fgetc(pr));//正常按顺序读
    printf("%c", fgetc(pr));//每次读取你会发现不会读取重复的,即文件指针也随着读取(或者写入)在变化
    //此时使用文件的随机读,所谓随机,即我们自己去控制
    //使用函数int fseek( FILE *stream, long offset, int origin );
    //origin:SEEK_CUR -文件当前 SEEK_END 末尾 SEEK_SET 开始  offset:偏移量
    //此时文件指针应该指向的是    h的位置,我们让它指向o
    fseek(pr, 2, SEEK_CUR);//为了让其指向o,自然从文件指针当前指向位置向后偏移两个单位即可
    printf("%c", fgetc(pr));//此时读取的就是o了,字符串和写入也是如此
    fclose(pr);
    pr = NULL;
    return 0;

下面还有几个方便使用的函数:

2.ftell

long ftell( FILE *stream );
返回当前位置与起始位置的偏移量

3.rewind

让文件回归到起始位置 void rewind(FILE *stream) 文件指针的位置回到起始位置。 

以上就是随机读写的全部知识啦,自己写的不是很好,嘿嘿,谢谢指正说明啦~

五、文件缓冲区 

在c语言中会自动为程序中使用的文件开辟缓冲区 是在内存里面
而读取的时候 先将硬盘中数据放入输入缓冲区,放满,在放入程序里面,反之同理。

一下代码只是提供测试,在刷新缓存区,或者关闭文件操作都可以是直接将缓存区的数据给文件或者程序的。 

#include <stdio.h>
#include <windows.h>
//VS2013 WIN10环境测试

int main()
{
    FILE* pf = fopen("test.txt", "w");
    fputs("abcdef", pf);//先将代码放在输出缓冲区

    printf("睡眠20秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
    Sleep(20000);//睡眠10秒

    printf("刷新缓冲区\n");
    fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
    //注:fflush 在高版本的VS上不能使用了
    printf("再睡眠20秒-此时,再次打开test.txt文件,文件有内容了\n");
    Sleep(20000);

    fclose(pf);
    //注:fclose在关闭文件的时候,也会刷新缓冲区
    pf = NULL;
    return 0;
}


所以得到结论:想让数据瞬间到文件里面去,刷新数据或者关闭文件即可,如果不关闭,可能造成数据丢失。 


http://chatgpt.dhexx.cn/article/DcEAu9jJ.shtml

相关文章

C语言 文件操作

文件操作在我们的使用中虽然占的不多&#xff0c;但是有些时候&#xff0c;还是需要这部分的知识的。比如&#xff0c;我们做了一个C语言版的售卖系统。现在的想法是在关闭的时候保存这次的结果&#xff0c;然后下一次继续使用&#xff0c;这个时候&#xff0c;就可以使用文件操…

手把手教你学Python之文件操作(一文掌握文件操作)

目录 1、Python中文本文件的读写 2、Python中常用的文件与文件夹操作方法 3、Python中Excel文件的读写 4、Python读取Excel文件案例​ 为了保存数据&#xff0c;方便修改和分享&#xff0c;数据通常以文件的形式存储在磁盘等外部存储介质中。根据逻辑上编码不同可将文件大致…

Linux文件操作

目录 一、系统api与库函数的关系二、open和close函数介绍2.1 open方法介绍2.2 close方法介绍 三、open/create函数创建文件时的权限设置四、read和write函数的介绍和使用4.1 read函数介绍4.2 write函数介绍4.3 如何使用 五、lseek函数的介绍和使用5.1 介绍5.2 如何使用 六、阻塞…

python文件操作(一看就懂)

python文件操作 历史遗留问题文件介绍打开文件转定义字符Python格式化字符串的替代符以及含义Python的转义字符及其含义 &#x1f495;Final~ 历史遗留问题 现在我们接着上文讲&#xff0c;如果没有看过我上篇文章的萌新朋友们可以先去看看&#xff0c;不然一会儿容易读的你满…

Linux的文件操作命令

接下来将介绍Linux中一些常用的文件操作命令&#xff0c;今天先演示五个类型的命令操作&#xff0c;内容不是特别的全&#xff0c;找的几个典型的常用的演示&#xff0c;想要全面的可以另找资料了解一下&#xff0c;推荐大家跟着敲跟着做才能加深记忆&#xff0c;祝大家学的开心…

Linux —— 文件操作

目录 1.内核提供的文件系统调用 1.1open和close 1.2标记位 1.3write和read 2.文件描述 2.1文件描述符 2.2文件描述符分配规则 3.重定向 3.1最“挫”的重定向 3.2使用系统调用 3.3重定向原理 3.4让我们的"shell"支持重定向操作 4.一切皆文件 5.缓冲区 5…

文件操作详解

1.文件的概念 文件的基本概念   所谓“文件”是指一组相关数据的有序集合。 这个数据集有一个名称&#xff0c;叫做文件名。实际上在前面的各章中我们已经多次使用了文件&#xff0c;例如源程序文件、目标文件、可执行文件、库文件 (头文件)等。文件通常是驻留在外部介质(如磁…

文件操作(C语言)

目录 一、前言 二、文件的打开和关闭 三、文件的读写 1.文件的顺序读写 2.文件的随机读写 四、文件结束的判定 一、前言 当我们向写好的通讯录程序中输入信息时&#xff0c;有时我们希望能保存输入的信息&#xff0c;不用每次打开这个程序都要重新输入&#xff0c;这时就…

文件操作详解(超级详细)

正常我们的程序在执行的时候程序结束后&#xff0c;会将所有数据清楚&#xff0c;那么我们应该如何保存数据呢&#xff1f;这里我们就需要用文件操作。 一、文件的打开和关闭 1.文件打开、关闭函数—fopen、fclose //打开文件 FILE * pf fopen ( const char * filename, const…

虚拟机安装黑苹果【虚拟机安装,黑苹果安装,黑苹果无法全屏问题】(这应该全网最全的资源了吧~)

一&#xff0c;资源下载 链接&#xff1a;https://pan.baidu.com/s/1cyGiDvkJinx8dnB57gO7UQ 提取码&#xff1a;i615 PS&#xff1a;里面有虚拟机资源&#xff0c;Drawin.ios文件和苹果镜像文件 这里只讲如何用虚拟机安装黑苹果&#xff0c;不讲如何安装虚拟机&#xff0c;下…

VMware虚拟机安装黑苹果10.15 || AMD R7 5800处理器

VMwar版本&#xff1a;16Pro 链接&#xff1a;https://pan.baidu.com/s/1qGHEynWSV4YS9WSNonxiuA?pwdazvh 提取码&#xff1a;azvh macOS链接&#xff0c;版本为Catalina 10.5。这个版本后&#xff0c;os安装包就过10G了&#xff0c;会特别卡。 链接&#xff1a;https://pan…

VMware16安装macOS10.15.1 - 黑苹果 - osx虚拟机

效果图 步骤 下载安装VMware&#xff0c;并且利用unlocker开启macos的支持VMware利用unlocker开启MacOS支持_Rudon滨海渔村的博客-CSDN博客效果步骤下载安装vmware16http://www.winwin7.com/soft/17946.html下载unlocker3.0 &#xff08;这里包括了darwin.iso和darwinPre15.is…

vm虚拟机15.5安装黑苹果maxos-10.14

鼎峰_小配针对vm虚拟机安装黑苹果maxos在学习和研究探索的过程中的一点点心得&#xff0c;从刚接触时的一脸懵逼&#xff0c;到后来慢慢的了解了&#xff0c;废话不多说&#xff0c;直接上干货。 一、需要准备的软件&#xff1a; a)解锁虚拟机MAC补丁&#xff1a;Unlocker 3.0…

图文详解如何在VMware Workstation11虚拟机上安装黑苹果Mac OS X 10.10系统

想要体验黑苹果系统当然要借助于VMware虚拟机了&#xff0c;那么下面大家就来看看亦是美网络小编的图文详解如何在VMware Workstation11虚拟机上安装黑苹果Mac OS X 10.10系统的教程吧&#xff01; 小编的安装环境&#xff1a; 实体机windows8.1专业版 VMware workstation 1…

win10上的VMware安装黑Mac(黑苹果、AMD)

使用AMD处理器的笔记本&#xff0c;在虚拟机上安装MAC操作系统。 本机安装配置&#xff1a; 华硕天选 AMD Ryzen 4800H 24G运存 Mac OS 10.13 High Sierra 1. 下载所需资源 所需资源下载链接&#xff1a; mac os 10.13镜像&#xff0c;其他工具 链接: https://pan.baidu.com/s/…

VMware虚拟机安装macOS黑苹果教程,亲测流程,全过程问题解决方案记录

先介绍流程,安装过程中遇到的问题都在最后列出并写出解决方案,找解决方案的直接到最后 准备: 1.VMware虚拟机 我的比较老,为VMware Workstation 14 Pro 14.1.3 build-9474260 2.unlocker 解锁VM的macOS系统 3.macOS的小白版cdr安装包(推荐)或者macOS的dmg安装包 4.如果…

Windows下VMmare黑苹果macOS Catalina 10.15虚拟机安装VMware tools工具

请先安装好macOS Catalina 10.15虚拟机。 VMware 10.15.5安装macOS Catalina 10.15请参考我的博客 Windows下VMware Workstations Pro15.5.0安装macOS Catalina 10.15虚拟机&#xff08;详细教程&#xff09; 如果VMware安装的虚拟机不安装VMware tools工具将无法全屏、进行虚…

Windows 10虚拟机Vmware 安装 黑苹果macos10.14

工具链接 提取码&#xff1a;zko1 VMware16.2.3安装 VMware16.2.3 连接手机有问题&#xff0c;改用 VMware15.5.6-16341506 流程: 虚拟机 MacOS系统解锁创建虚拟机安装macOS安装VMware tools 虚拟机 MacOS系统解锁 使用工具&#xff1a;Unlocker.7z 停止VM服务:停止所有VMwa…

黑苹果从入门到精通:最详细的VMware安装macOS教程

前言 不知为何&#xff0c;以前我发的两篇关于黑苹果的文章或没过审或被删除&#xff0c;最近SMZDM上有不少优质的黑苹果文章发出来&#xff0c;貌似禁令已开&#xff0c;前段时间在一篇写的很不错的黑果文章下吹牛说今年要写一个系列&#xff0c;故有了这篇文章作为系列的开头…

VMware虚拟机安装黑苹果MacOS Mojave系统详细教程

更多资源请百度搜索&#xff1a;前端资源网 欢迎关注我的博客&#xff1a;www.w3h5.com 大家好&#xff0c;我的主要更新渠道是自己搭建的博客&#xff0c;为了节省流量&#xff0c;我开了防盗链。。。所以其他平台可能会有图片不能显示的问题&#xff0c;请转至我的博客进行查…