C语言之“文件操作”

文章目录

  • [1. 什么是文件?](#1. 什么是文件?)
    • [(1. 为什么使用文件?](#(1. 为什么使用文件?)
    • (2.什么是文件
      • [2.1 程序文件](#2.1 程序文件)
      • [2.2 数据文件](#2.2 数据文件)
      • [2.3 文件名](#2.3 文件名)
  • [2. 二进制文件和文本文件?](#2. 二进制文件和文本文件?)
  • [3. 文件的打开和关闭](#3. 文件的打开和关闭)
    • [3.1 流和标准流](#3.1 流和标准流)
    • [3.2 文件指针(用来管理流的)](#3.2 文件指针(用来管理流的))
    • [3.3 文件的打开与关闭](#3.3 文件的打开与关闭)
  • [4. 文件的顺序读写](#4. 文件的顺序读写)
    • 顺序读写函数介绍
    • [4.1 fputc(字符输出函数)](#4.1 fputc(字符输出函数))
    • [4.2 fgetc(字符输入函数)](#4.2 fgetc(字符输入函数))
    • [4.3 fputs(文本输出函数)](#4.3 fputs(文本输出函数))
    • [4.4 fgets(文本行输入函数)](#4.4 fgets(文本行输入函数))
    • [4.5 fprintf(格式化输入函数)](#4.5 fprintf(格式化输入函数))
    • [4.6 fscanf(格式化输出函数)](#4.6 fscanf(格式化输出函数))
  • [5. 文件的随机顺序读写](#5. 文件的随机顺序读写)
    • [5.1 fseek](#5.1 fseek)
    • [5.2 ftell](#5.2 ftell)
    • [5.3 rewind](#5.3 rewind)
  • [6. 文件读取结束的判定](#6. 文件读取结束的判定)
  • [7. 文件缓冲区](#7. 文件缓冲区)

1. 什么是文件?

(1. 为什么使用文件?

c 复制代码
	int a = 0;
	printf("a=%d\n", a);
	scanf("%d\n", &a);
	printf("a=%d\n", a);

在上面的代码中,运行一次之后,下次运行,a仍然先显示的是0。为什么还是0呢?因为我们这些数据是放在内存里的。

如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。

那为什么我们制作的表格和ppt能够一直存在?因为这些是放在电脑的硬盘上。

我们发现,数据放在文件中,文件再放在硬盘中,就可以将数据持久化的保存。

(2.什么是文件

磁盘(硬盘)上的文件是文件。

但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。

2.1 程序文件

程序文件包括源程序文件 (后缀为.c),目标文件 (windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

2.2 数据文件

文件的内容不一定是程序,还有可能是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

本章讨论的是数据文件。

在以前各章所处理数据的输入输出都是以终端(就是vs的黑框框,屏幕)为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。

其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件

2.3 文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。

文件名包含3部分:文件路径,文件名主干,文件后缀

例如: c:\code\test.txt

为了方便起见,文件标识 常被称为文件名

2. 二进制文件和文本文件?

(1)根据数据的组织形式(即内容),数据文件被称为文本文件或者二进制文件

数据在内存中以二进制 的形式存储,如果不加转换的输出到外存的文件中 ,就是二进制文件

如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件

(2)一个数据在文件中是怎么存储的呢?

  • 字符 一律以ASCII形式存储
  • 数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节。

3. 文件的打开和关闭

3.1 流和标准流

我们程序的数据需要输出 到各种外部设备(文件,U盘,屏幕),也需要从外部设备获取数据。不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了"流"的概念,我们可以把流想象成河(流淌着字符的河)。对于程序员来说,只要关注"流"就可以了。

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的。

一般情况下,我们要想 向"流"里写数据 或 从"流"中读取数据,都是(1)打开流,(2)操作[读 / 写]。(3)关闭流。

  1. 标准流

为什么我们从键盘输入数据(scanf),向屏幕上输出数据,并没有打开流呢?

那是因为C语言程序在启动的时候,默认打开3个流:

stdin - 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据 。(记忆方式:键盘从我们这里读取到了即将输入的数据)
stdout - 标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出流中
stderr - 标准错误流,大多数环境中输出到显示器界面。

这是默认打开了这三个流,我们使用scanf、printf等函数就可以直接进行输入输出操作的。

stdin、stdout、stderr 这三个标准流的类型是: FILE * ,通常称为文件指针(指向文件信息区)。

C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的。

3.2 文件指针(用来管理流的)

缓冲文件系统中,关键的概念是"文件类型指针",简称"文件指针"。

每个被使用的文件都在内存中开辟了一个相应的文件信息区 (每个文件都有文件信息区),用来存放 文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名 FILE。

例如,VS2013 编译环境提供的 stdio.h 头文件中有以下的文件类型申明(不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。):

c 复制代码
struct _iobuf {
    char *_ptr;
    int _cnt;
    char *_base;
    int _flag;
    int _file;
    int _charbuf;
    int _bufsiz;
    char *_tmpfname;
};
typedef struct _iobuf FILE;

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。

一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

下面我们可以创建一个FILE*的指针变量:FILE* pf;//文件指针变量

定义pf 是一个指向FILE类型数据的指针变量 。可以使pf(FILE* 文件指针)指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过(文件指针变量)能够间接找到(与它关联的文件)

3.3 文件的打开与关闭

  1. 在之后经常说,读和写,那什么是读和写呢?
  2. 打开文件

ANSIC 规定使用 fopen 函数来打开文件, fclose 来关闭文件。

c 复制代码
//打开文件                  文件名            打开方式
函数原型:FILE * fopen ( const char * filename, const char * mode );
//返回类型是文件指针FILE*
 FILE* PF=fopen("test.txt","w");

在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量(文件信息的地址)指向该文件,也相当于建立了指针和文件的关系。

如果文件打开成功,返回的是有效指针

如果文件打开失败,则返回NULL(是空指针的话,很危险,要记得判断)

''w''(写)的打开方式:

如果没有我们想打开的文件data.txt,而我们fopen这个文件,它会在这个项目下创立一个data.txt

如果有这个文件,不会新建。它不管里面有什么,它会将文件清空
"r"只读的打开方式:不会修改

  1. 打开的地点

第一种就在当前项目下FILE* fopen ( test.txt,"w" );

第二种是打开桌面上的文件 :这个需要使用绝对路径(右击文件,点击属性):路径\名字(双斜杠,防止变成转义字符)

c 复制代码
FILE* fopen("C:\\Users\\20439\\Desktop\\wen.docx","w");

第三种:当前项目的上一级(相对路径)

.表示当前路径,...表示上一级路径

.\\..表示这一级的上一级
.\\..\\..表示这一级的上一级的上一级

c 复制代码
FILE* fopen(".\\..\\dao.docx","w");
  1. 打开方式有哪些?

  2. 关闭文件

c 复制代码
int fclose(FILE* stream);

在关闭文件之后还需要置为空指针

c 复制代码
#include<stdio.h>
int main()
{
	FILE* pf = fopen("data.text", "w");//打开文件,返回的是有效指针
	//是否打开
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//.....
	
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

4. 文件的顺序读写

顺序读写函数介绍

4.1 fputc(字符输出函数)

fputc(只能写一个字符)
int fputc ( int character, FILE * stream );
参数说明:
character :要写入的字符,该字符会被转换为 unsigned char 类型,然后写入文件流。
stream :指向要写入的文件流的指针。
返回值:

如果成功, fputc 返回写入的字符。

如果发生错误, fputc 返回 EOFEnd Of File,通常定义为 -1 )。

1.黑框框里没有内容,说名打开文件没有问题

2.右击文件,点击"打开所在的文件夹",点开刚刚打开以及输入的文件

  1. 如果想输入很多个字符可以使用循环

4.2 fgetc(字符输入函数)

fgetc
int fgetc ( FILE * stream );
参数说明
stream:指向要从中读取字符的文件流的指针。
返回值

如果成功, fgetc 返回读取的字符的ASCII码(一个unsigned char类型的值,被转换为int类型)。

如果到达文件末尾或发生错误,fgetc返回EOF
读取一次,光标就会往后移动一格

c 复制代码
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	int i = 0;
	for (i = 'a'; i < 'z'; i++)
		fputc(i, pf);
	//在读取之前要关闭写句柄
	fclose(pf);
	pf = NULL;
	
//简单一点,用循环	
FILE* pf1 = fopen("test.txt", "r");
if (pf1 == NULL)
{
	perror("fopen");
	return 1;
}
int ch = 0;
while ((ch = fgetc(pf1)) != EOF)
	printf("%c ", ch);

	//读文件
	FILE* pf1 = fopen("test.txt", "r");
	int ch = fgetc(pf1);
	printf("%c\n", ch);
	ch = fgetc(pf1);
	printf("%c\n", ch); 
	ch = fgetc(pf1);
	printf("%c\n", ch);
	ch = fgetc(pf1);
	printf("%c\n", ch);
	ch = fgetc(pf1);
	printf("%c\n", ch);

	//关闭
	fclose(pf1);
	pf1 = NULL;
}

stdin----输入流------即读的形式打开文件-----文件的输入流

stdout--输出流------即写的形式打开文件-----文件的输出流

c 复制代码
int main()
{
	//fgetc读字符,去哪里读呀?()需要的是流
	int ch = fgetc(stdin);//从键盘(标准输入流)上读取
	//fputc打印,第一个参数字符,第二个参数流
	fputc(ch, stdout);//将字符输出(写)到屏幕(标准输出流)
	return 0;
}

4.3 fputs(文本输出函数)

记得先打开文件(打开方式:写),才能在流里写字符串

fputs(把字符串写到一个流里)

返回类型int----字符串的地址-------文件指针(关联到一个文件)

函数原型:int fputs ( const char * str, FILE * stream );
参数说明:
str:指向要写入的字符串的指针。
stream:指向要写入的文件流的指针。
返回值:

如果成功,fputs返回一个非负整数。

如果发生错误,fputs返回 EOF (通常定义为 -1 )
如果写两行fputs,这两次的内容会放在同一行,因为我们没有加换行符\n

4.4 fgets(文本行输入函数)

打开方式:读

fgets
char * fgets ( char * str, int num, FILE * stream );
参数说明:
str:指向用于存储读取数据的字符数组的指针。
num:指定要读取的最大字符数,包括字符串结束符 \0 。(实际读取了num-1个字符,因为最后一个是\0)
stream:指向要读取的文件流的指针。
返回值(接收与否均可):

如果成功,fgets返回指向str的指针。

如果到达文件末尾或发生错误,fgets返回 NULL 。

如果文件的内容有好几行,fgets只读取一行,\n也读取了,之后就结束了

c 复制代码
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[20] = { "xxxxxxxxxx" };
	fgets(arr, 5, pf);
	printf("%s\n", arr);

	//关闭文件
	fclose(pf);
	pf = NULL;
}

我们将num改成10,再将文件的内容改成换行的:

c 复制代码
int main()
{
	char arr[20] = { 0 };
	fgets(arr, 20, stdin);
	fputs(arr, stdout);
	return 0;
}

4.5 fprintf(格式化输入函数)

对比printf和fprintf
printf("%s %d\n", a, b); //是将内容输出到屏幕上
fprintf(pf,"%s %d",a, b); //是将内容写在文件里
fprintf
int fprintf ( FILE * stream, const char * format, ... );
int printf ( const char * format, ... );
参数说明:
stream :指向要写入的文件流的指针。
format :一个字符串,包含格式说明符和普通字符。格式说明符用于指定如何格式化后续的参数。
... :可变参数列表,包含要写入的值,这些值将根据 format 字符串中的格式说明符进行格式化。
fprintf 返回值:

如果成功, fprintf 返回写入的字符数。

如果发生错误, fprintf 返回一个负值。
printf 返回值:
printf 返回实际写入到标准输出的字符数。

c 复制代码
int main()
{
	char name[] = "houpp";
	int age = 20;
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fprintf(pf, "%s %d\n", name,age);//内容写入文件
    fclose(pf);
	pf = NULL;
}

4.6 fscanf(格式化输出函数)

fscanf
int fscanf ( FILE * stream, const char * format, ... );

fscanf返回值:
fscanf返回成功解析并赋值的输入项的数量。如果到达文件末尾或发生错误,则返回EOFEnd Of File,通常定义为 -1 )。

scanf返回值
scanf返回成功解析并赋值的输入项的数量。如果到达输入流的末尾或发生错误,则返回EOFEnd Of File,通常定义为 -1

c 复制代码
int main()
{
	char name[] = "houpp";
	int age = 20;
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//从文件中读取内容
	fscanf(pf, "%s %d", name, &age);
	//打印在屏幕上
	printf("%s %d", name, age);

	//fprintf(pf, "%s %d\n", name,age);//内容写入文件
    fclose(pf);
	pf = NULL;
}

对比一组函数

c 复制代码
scanf/fscanf/sscanf

printf/fprintf/sprintf

scanf/printf:针对 标准输入流/标准输出流 的 格式化输入/输出函数
fscanf/fprintf:针对 所有输入流/输出流 的 格式化输入/输出函数(也就是可以针对文件也可以针对标准输入/出流)
sscanf/sprintf:将格式化的数据转换成字符串,或者从字符串中提取格式化的数据
scanf
int scanf ( const char * format, ... );

fscanf
int fscanf ( FILE * stream, const char * format, ... );

sprintf
int sprintf ( char * str, const char * format, ... );
sprintf实际上就是将格式化的数据写到字符串中,可以理解为将格式化的数据转换成字符串

sprintf

是将格式化的数据放在数组里【格式化的数据---------写到---------->字符串】

c 复制代码
int main()
{
	char name[] = "houpp";
	int age = 20;

	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	char arr[100] = { 0 };
	sprintf(arr, "%s %d", name, age);
	//看一下数组里有没有内容了
	printf("%s\n", arr);

    fclose(pf);
	pf = NULL;
}
c 复制代码
struct S {
	char name[20];
	int age;
	float score;
};

int main() {
	char arr[100] = { 0 };
	struct S s = { "wangwu",23,66.6f };
	//printf("%s %d %f", s.name, s.age, s.score);
	sprintf(arr, "%s %d %f", s.name, s.age, s.score);

	printf("%s\n", arr);

	return 0;
}
c 复制代码
struct S {
	char name[20];
	int age;
	float score;
};

int main() {
	char arr[100] = { 0 };
	struct S s = { "wangwu",23,66.6f };
	//创建临时变量
	struct S tmp = { 0 };

	//将S中的各个数据转换成字符串,存放在arr中
	sprintf(arr, "%s %d %f", s.name, s.age, s.score);

	//从字符串arr中提取格式化的数据,存放在tmp中
	//scanf("%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
	sscanf(arr, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
	printf("%s %d %f", tmp.name, tmp.age, tmp.score);

	return 0;
}

4.7 fwrit

fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
参数说明:
ptr :指向要写入数据的内存块的指针。
size :每个数据项的大小(以字节为单位)。
count :要写入的数据项的数量。
stream :指向要写入数据的文件流的指针。
返回值:
fwrite 返回成功写入的数据项的数量。如果发生错误或到达文件末尾,则返回一个小于 count 的值。

就是把ptr指向空间里面,count个大小为size个字节的元素的数据,写到stream里面去。

c 复制代码
struct S {
	char name[20];
	int age;
	float score;
};

int main() {
	struct S s = { "cuihua", 25, 88.8f };
	//以二进制形式写进文件

	//1.打开文件
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL) {
		perror("fopen");
		return 1;
	}
	//2.写文件
	fwrite(&s, sizeof(struct S), 1, pf);

	//3.关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

打开文件:乱码是因为写的是二进制字符的原因。

4.7 fread

fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
参数说明:
ptr :指向用于存储读取数据的内存块的指针。
size :每个数据项的大小(以字节为单位)。
count :要读取的数据项的数量。
stream :指向要从中读取数据的文件流的指针。
返回值:
fread 返回成功读取的数据项的数量。如果到达文件末尾或发生错误,则返回一个小于 count 的值。

就是把steam指向空间里面(也就是文件里面读取),count个大小为size个字节的元素的数据,写到ptr里面去。

c 复制代码
struct S {
	char name[20];
	int age;
	float score;
};

int main() {
	struct S s = { "cuihua", 25, 88.8f };
	//读取二进制形式的信息写进文件中

	//1.打开文件
	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL) {
		perror("fopen");
		return 1;
	}
	//2.读文件
	fread(&s, sizeof(struct S), 1, pf);

	printf("%s %d %f\n", s.name, s.age, s.score);

	//3.关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

5. 文件的随机顺序读写

5.1 fseek

根据文件指针的位置和偏移量来定位文件指针(文件内容的光标)。

c 复制代码
int fseek ( FILE * stream, long int offset, int origin );

参数说明:
stream :指向要操作的文件流的指针。
offset :要移动的字节数

如果 originSEEK_SET ,则 offset 是从文件开始处的偏移量;

如果 originSEEK_CUR ,则 offset 是从当前文件指针位置的偏移量;

如果 originSEEK_END ,则 offset 是从文件末尾的偏移量。
origin :指定 offset 的基准位置,可以是以下三个值之一:
SEEK_SET :从文件的开始位置移动。
SEEK_CUR :从当前文件指针位置移动。
SEEK_END :从文件的末尾位置移动。
返回值
fseek 成功时返回 0 ,失败时返回非零值。

c 复制代码
int main() {
	//1.打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL) {
		perror("fopen");
		return 1;
	}
	//2.读文件
	int ch = 0;
	ch = fgetc(pf);//a
	printf("%c\n", ch);
	ch = fgetc(pf);//b
	printf("%c\n", ch);

	//3.关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

我们要是想打印abe呢?

这个时候就要用到fseek函数了。

c 复制代码
int main() {
	//1.打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL) {
		perror("fopen");
		return 1;
	}
	//2.读文件
	int ch = 0;
	ch = fgetc(pf);//a
	printf("%c\n", ch);
	ch = fgetc(pf);//b
	printf("%c\n", ch);

	//定位文件指针
	fseek(pf, 4, SEEK_SET);
    //fseek(pf, 2, SEEK_CUR);
    //fseek(pf, -2, SEEK_END);
	ch = fgetc(pf);//e
	printf("%c\n", ch);

	//3.关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

5.2 ftell

返回文件指针相对于起始位置的偏移量

c 复制代码
long int ftell ( FILE * stream );

参数说明:
stream :指向要查询的文件流的指针。

返回值:
ftell 返回当前文件指针的位置(以字节为单位),如果发生错误,则返回 -1L

c 复制代码
int main() {
	//1.打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL) {
		perror("fopen");
		return 1;
	}
	//2.读文件
	int ch = 0;
	ch = fgetc(pf);//a
	printf("%c\n", ch);
	ch = fgetc(pf);//b
	printf("%c\n", ch);

	//定位文件指针
	fseek(pf, 4, SEEK_SET);
	ch = fgetc(pf);//e
	printf("%c\n", ch);

	//输出文件指针相较于文件起始位置的偏移量
	printf("%d\n", ftell(pf));

	//3.关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

为什么是5呢?

5.3 rewind

让文件指针的位置回到文件的起始位置

c 复制代码
void rewind ( FILE * stream );
c 复制代码
int main() {
	//1.打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL) {
		perror("fopen");
		return 1;
	}
	//2.读文件
	int ch = 0;
	ch = fgetc(pf);//a
	printf("%c\n", ch);
	ch = fgetc(pf);//b
	printf("%c\n", ch);

	//定位文件指针
	fseek(pf, 4, SEEK_SET);
	ch = fgetc(pf);//e
	printf("%c\n", ch);

	//输出文件指针相较于文件起始位置的偏移量
	printf("%d\n", ftell(pf));

	//将文件指针重新定位到文件的起始位置
	rewind(pf);
	ch = fgetc(pf);//a
	printf("%c\n", ch);


	//3.关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

6. 文件读取结束的判定

7. 文件缓冲区

相关推荐
一个不正经的林Sir2 分钟前
C#WPF基础介绍/第一个WPF程序
开发语言·c#·wpf
API快乐传递者6 分钟前
Python爬虫获取淘宝详情接口详细解析
开发语言·爬虫·python
公众号Codewar原创作者8 分钟前
R数据分析:工具变量回归的做法和解释,实例解析
开发语言·人工智能·python
赵钰老师11 分钟前
基于R语言APSIM模型应用及批量模拟(精细农业、水肥管理、气候变化、粮食安全、土壤碳周转、环境影响、农业可持续性、农业生态等)
开发语言·数据分析·r语言
lly20240628 分钟前
Highcharts 饼图:数据可视化利器
开发语言
lw向北.34 分钟前
Qt For Android之环境搭建(Qt 5.12.11 Qt下载SDK的处理方案)
android·开发语言·qt
IT女孩儿1 小时前
JavaScript--WebAPI查缺补漏(二)
开发语言·前端·javascript·html·ecmascript
m0_748238921 小时前
webgis入门实战案例——智慧校园
开发语言·ios·swift
悲伤小伞1 小时前
C++_数据结构_详解二叉搜索树
c语言·数据结构·c++·笔记·算法
Clockwiseee1 小时前
PHP伪协议总结
android·开发语言·php