【C语言】文件操作-程序员宅基地

技术标签: 学习  c语言  

1. 为什么使用文件?

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

2. 什么是文件?

磁盘(硬盘)上的⽂件是⽂件。
但是在程序设计中,我们⼀般谈的⽂件有两种:程序⽂件、数据⽂件(从⽂件功能的⻆度来分类的)。

2.1 程序文件

程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows 环境后缀为.exe)。

2.2 数据文件

⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或者输出内容的⽂件。
本章讨论的是数据⽂件。
在以前各章所处理数据的输⼊输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显⽰到显⽰器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处理的就是磁盘上⽂件。

2.3  文件名

⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤。
⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀
例如: c:\code\test.txt
为了⽅便起⻅,⽂件标识常被称为⽂件名。

3. 二进制文件和文本文件

  • 二进制文件:可以存储各种类型的数据,如整数、浮点数等,每条数据的长度不一定相同,它们通常需要特定的程序或解码器来读取和写入。此外,操作系统对二进制文件中的换行符处理方式与文本文件不同,例如在Windows系统中,不会对二进制文件中的换行符进行转换。
  • 文本文件:只能存储字符型数据,且每条数据通常是固定长度的。文本文件可以直接使用文本编辑器进行读写,因为它们以人类可读的形式存储信息。在Windows系统中,文本文件在写入时会将换行符转换为“\r”,而在读取时会将其转换回原来的换行符。

4. ⽂件的打开和关闭

4.1 流和标准流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出 操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。
C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。
⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

4.2 标准流

那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语⾔程序在启动的时候,默认打开了3个流:
stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出 流中。
stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯。
这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为⽂件指针。
C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的。

4.2 文件指针

缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”。
每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名 FILE.
例如,VS2013 编译环境提供的 stdio.h 头⽂件中有以下的⽂件类型申明:
struct _iobuf {
 char *_ptr;
 int _cnt;
 char *_base;
 int _flag;
 int _file;
 int _charbuf;
 int _bufsiz;
 char *_tmpfname;
 };
typedef struct _iobuf FILE;
不同的C编译器的FILE类型包含的内容不完全相同,但是⼤同⼩异。
每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信
息,使⽤者不必关⼼细节。
⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便。
下⾯我们可以创建⼀个FILE*的指针变量:
FILE* pf;/

基本上每个文件都有一个文件信息区,这个文件信息区是一个FILE类型的结构体,它把这个结构体的地址放到pf,就可以通过pf去找到这个文件信息区,然后通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过⽂件指针变量能够间接找到与它关联的⽂件。

4.3 文件的打开和关闭

⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。
在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了指针和⽂件的关系。
ANSI C 规定使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件。
//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );
mode表⽰⽂件的打开模式,下⾯都是⽂件的打开模式:
  1. "r":以只读方式打开文件(默认为文本模式)。
  2. "w":以写入方式打开文件,如果文件存在则清空内容,如果文件不存在则创建新文件(默认为文本模式)。
  3. "a":以追加方式打开文件,如果文件不存在则创建新文件(默认为文本模式)。
  4. "r+":以读写方式打开文件,如果文件不存在则失败(默认为文本模式)。
  5. "w+":以读写方式打开文件,如果文件存在则清空内容,如果文件不存在则创建新文件(默认为文本模式)。
  6. "a+":以读写方式打开文件,如果文件不存在则创建新文件(默认为文本模式)。
  7. "rb"(只读二进制模式):以只读方式打开一个二进制文件,如果文件不存在则打开失败。这种模式下,只能从文件中读取数据,文件指针默认位于文件开头。
  8. "wb"(只写二进制模式):以写入方式打开一个二进制文件,如果文件存在则会清空内容,如果文件不存在则会创建新文件。这种模式下,只能向文件中写入数据。
  9. "ab"(追加二进制模式):以追加方式打开一个二进制文件,如果文件不存在则会创建新文件。这种模式下,可以向文件末尾追加数据,但不会自动清空原有内容。
  10. "rb+"(读写二进制模式):以读写方式打开一个二进制文件,如果文件不存在则打开失败。这种模式下,可以从文件中读取数据,也可以向文件中写入数据,文件指针可以自由移动。
  11. "wb+"(写读二进制模式):以读写方式打开或创建一个二进制文件,如果文件存在则会清空内容。这种模式下,可以读取已有数据,也可以在任意位置写入数据。
  12. "ab+"(追加读写二进制模式):以读写方式打开一个二进制文件,如果文件不存在则会创建新文件。这种模式下,可以在文件末尾追加数据,也可以读取文件中的数据。

在使用 fopen 函数时,需要根据实际需求选择合适的 mode 参数,以确保文件能够按照预期的方式进行操作。同时,由于 fopen 函数可能会因为各种原因(如文件不存在、权限不足等)而失败,因此在调用后应该检查返回值,确保文件成功打开。

int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

5. 文件的读写顺序

5.1 顺序读写函数的介绍

函数名
功能
适⽤于
fgetc
字符输⼊函数
所有输⼊流
fputc
字符输出函数
所有输出流
fgets
⽂本⾏输⼊函数
所有输⼊流
fputs
⽂本⾏输出函数
所有输出流
fscanf
格式化输⼊函数
所有输⼊流
fprintf
格式化输出函数
所有输出流
fwrite
⼆进制输出
⽂件
fread
⼆进制输⼊
⽂件

5.1.1 fgetc 和 fputc

  1. fgetc:这个函数用于从指定的文件流中读取一个字符。它的函数原型为 int fgetc(FILE *stream),其中 stream 是指向要读取的文件的指针。如果成功读取一个字符,fgetc会返回该字符的ASCII码值;如果遇到文件末尾或读取失败,则返回EOF(通常是-1)。每次调用fgetc后,文件内部的读指针会自动向后移动一个字节的位置。
  2. fputc:这个函数用于将一个字符写入到指定的文件流中。它的函数原型为 int fputc(int c, FILE *stream),其中 c 是要写入的字符,而 stream 是指向要写入的文件的指针。如果写入成功,fputc会返回写入的字符的ASCII码值;如果写入失败,则返回EOF(通常是-1)。
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	for (char ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ret;
	while((ret = fgetc(pf))!=EOF)
	{
		printf("%c", ret);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

5.1.2 fgets 和 fputs 

  1. fgets:这个函数用于从指定的文件流中读取一行字符串。它的函数原型为 char *fgets(char *str, int n, FILE *stream),其中 str 是指向存储读取结果的字符数组的指针,n 是指定的最大读取字符数(通常为n-1,因为需要为结尾的空字符'\0'预留位置),stream 是指向要读取的文件的指针。如果成功读取,fgets会将读取的字符串加上结尾的空字符'\0'存储到str指向的数组中,并返回该数组的地址;如果遇到文件末尾或读取失败,则返回NULL。fgets在读取过程中,遇到换行符、文件结束或者达到最大读取字符数时会停止读取。
  2. fputs:这个函数用于将一个字符串写入到指定的文件流中。它的函数原型为 int fputs(const char *str, FILE *stream),其中 str 是要写入的字符串(以空字符'\0'结尾),stream 是指向要写入的文件的指针。fputs会将字符串写入到文件中,直到遇到空字符'0'结束。如果写入成功,fputs返回非负值;如果写入失败,返回EOF(通常是-1)。
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	fputs("hello\n", pf);
	fputs("world", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

 

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[20] = "xxxxxxxxxxxxxxxxxxx";
	fgets(arr, 10, pf);

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

 fgets函数只会读一行,当读完一行的所有字符后,会在后面补上一个‘\0’。再次调用fgets函数时,它则会读取下一行的数据。

5.1.3 fprintf 和 fscanf 

  1. fprintf

    • 功能:将格式化的数据写入到指定的文件中。
    • 语法int fprintf(FILE *fp, const char *format, ...);
    • 参数fp 是一个指向 FILE 类型文件的指针,format 是一个字符串,定义了后续参数的输出格式,后续参数是可变的,对应于 format 字符串中的格式说明符。
    • 返回值:成功写入的字符数,如果发生错误则返回一个负值。
  2. fscanf

    • 功能:从指定的文件中按照给定的格式读取数据。
    • 语法int fscanf(FILE *fp, const char *format, ...);
    • 参数fp 是一个指向 FILE 类型文件的指针,format 是一个字符串,定义了读取数据的格式,后续参数是地址列表,用于存储从文件中读取的值。
    • 返回值:成功读取并赋值的数据项个数,如果到达文件末尾或发生错误则返回 EOF
struct S
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct S s = { "小明",18,80.0f };
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fprintf(pf, "%s %d %f", s.name, s.age, s.score);
	fclose(pf);
	pf = NULL;
	return 0;
}

 

struct S
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct S s = { 0 };
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fscanf(pf, "%s %d %f", &s.name, &s.age, &s.score);

	fclose(pf);
	pf = NULL;
	return 0;
}

 5.1.4 fwrite 和 fread

  1. fwrite

    • 功能:将一个数据块写入到指定的文件中。
    • 语法size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream);
    • 参数buffer 是一个指向要写入数据的指针,size 是每个数据项的大小(以字节为单位),count 是要写入的数据项的数量,stream 是指向 FILE 类型文件的指针。
    • 返回值:成功写入的数据项个数,如果发生错误则返回一个小于 count 的值。
  2. fread

    • 功能:从指定的文件中读取一个数据块。
    • 语法size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
    • 参数buffer 是一个指向存储读取数据的内存区域的指针,size 是每个数据项的大小(以字节为单位),count 是要读取的数据项的数量,stream 是指向 FILE 类型文件的指针。
    • 返回值:成功读取并赋值的数据项个数,如果到达文件末尾或发生错误则返回一个小于 count 的值。

int main()
{
	int arr[] = { 1,2,3,4,5 };
	FILE* pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int sz = sizeof(arr) / sizeof(arr[0]);
	fwrite(arr, sizeof(arr[0]), sz, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

int main()
{
	int arr[5] = { 0 };
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int sz = sizeof(arr) / sizeof(arr[0]);
	fread(arr, sizeof(arr[0]), sz, pf);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

6. 文件的随机读写

6.1 fseek

根据⽂件指针的位置和偏移量来定位⽂件指针。
int fseek ( FILE * stream, long int offset, int origin );

其中:

  1. stream:是一个指向 FILE 对象的指针,该对象标识了流(文件)。
  2. offset:是相对于 fromwhere 的位移量,以字节为单位。
  3. origin:定义了 offset 的起始位置,它可以是以下三个值之一:
    • SEEK_SET:从文件开头开始计算偏移量。
    • SEEK_CUR:从当前位置开始计算偏移量。
    • SEEK_END:从文件末尾开始计算偏移量。

如果fseek函数执行成功,它将返回0;如果执行失败(例如,当偏移量超出文件大小),则不改变文件指针的位置,并返回非零值。

一个常见的fseek使用场景是在文件中插入或删除数据。例如,如果你想在文件的特定位置后追加一些内容,你可以使用fseek将文件指针移动到那个位置,然后写入新的内容。

需要注意的是,在使用fseek时,要确保对文件的操作符合文件打开模式。例如,以只读模式打开的文件不能使用fseek来设置写操作的位置。此外,fseek函数与文本模式和二进制模式的兼容性也是需要注意的,因为在文本模式下,某些特殊的字符(如换行符)可能会被转换,这可能会影响到fseek的行为。

6.2 ftell

返回⽂件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
int main()
{
	FILE* pFile;
	long size;
	pFile = fopen("data.txt", "rb");
	if (pFile == NULL)
		perror("Error opening file");
	else
	{
		fseek(pFile, 0, SEEK_END); // non-portable
		size = ftell(pFile);
		fclose(pFile);
		printf("Size of data.txt: %ld bytes.\n", size);
	}
	return 0;
}

6.3 rewind 

让⽂件指针的位置回到⽂件的起始位置
void rewind ( FILE * stream );

7. 文件结束的判定

打开一个流的时候这个流上有2个标记值:

1. 是否遇到文件末尾

2. 是否发生错误

feof 用来检测第一个标记值

ferror 用来检测第二个标记值

8. 文件缓冲区

ANSIC 标准采⽤“缓冲⽂件系统” 处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存为
程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓
冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输 ⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。

文件缓冲区的作用主要是提高数据读写效率和保护硬盘

文件缓冲区是计算机内存中的一块区域,它用于临时存储从磁盘文件中读取的数据或待写入的数据。以下是文件缓冲区的几个关键作用:

  1. 减少磁盘I/O操作:由于磁盘的读写速度远低于内存的访问速度,使用缓冲区可以减少对磁盘的直接访问次数。当需要读取文件时,数据先被加载到缓冲区中,程序直接从缓冲区读取数据,而不是每次都去访问物理硬盘。
  2. 提高数据处理速度:缓冲区使得数据的处理可以在更快的内存中进行,从而提高整个系统的运行效率。例如,在文本编辑器中编辑文档时,实际上是在缓冲区中进行修改,直到用户明确保存文件时,修改才被写回硬盘。
  3. 统一数据传输速率:缓冲区还用于在不同速率的设备之间传输数据时提供速度适配。比如,它可以协调慢速的硬盘与快速的CPU之间的数据传输速率差异。
  4. 缓存常用数据:缓冲区可以暂存那些经常被重复使用的数据,避免每次需要时都重新从硬盘加载,这在数据库系统和文件系统中尤为重要。
  5. 缓冲区刷新策略:缓冲区根据不同的应用场景采取不同的刷新策略,如立即刷新、行刷新或全缓冲刷新等,以确保数据的及时更新和减少数据丢失的风险。

综上所述,文件缓冲区通过在内存中暂存数据来桥接快速和慢速设备之间的速度差异,减少了对硬盘的物理读写次数,从而提升了数据处理的效率和系统的整体性能。

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_58252863/article/details/137245470

智能推荐

基于Java在线电影票购买系统设计实现(源码+lw+部署文档+讲解等)-程序员宅基地

文章浏览阅读4.1k次,点赞2次,收藏4次。社会和科技的不断进步带来更便利的生活,计算机技术也越来越平民化。二十一世纪是数据时代,各种信息经过统计分析都可以得到想要的结果,所以也可以更好的为人们工作、生活服务。电影是生活娱乐的一部分,特别对喜欢看电影的用户来说是非常重要的事情。把计算机技术和影院售票相结合可以更符合现代、用户的要求,实现更为方便的购买电影票的方式。本基于Java Web的在线电影票购买系统采用Java语言和Vue技术,框架采用SSM,搭配MySQL数据库,运行在Idea里。

集合的addAll方法--list.addAll(null)会报错--java.lang.NullPointerException-程序员宅基地

文章浏览阅读1.8k次。Exception in thread "main" java.lang.NullPointerException at java.util.ArrayList.addAll(ArrayList.java:559) at com.iflytek.epdcloud.recruit.utils.quartz.Acool.main(Acool.java:16)import java.u..._addall(null)

java获取当天0点到24点的时间戳,获得当前分钟开始结束时间戳_java 获取某分钟的起止时间戳-程序员宅基地

文章浏览阅读4.5k次。public static void main(String[] args) { Calendar todayStart = Calendar.getInstance(); todayStart.set(Calendar.HOUR_OF_DAY, 0); todayStart.set(Calendar.MINUTE, 0); toda..._java 获取某分钟的起止时间戳

北京内推 | 京东AI研究院计算机视觉实验室招聘三维视觉算法研究型实习生-程序员宅基地

文章浏览阅读1.1k次。合适的工作难找?最新的招聘信息也不知道?AI 求职为大家精选人工智能领域最新鲜的招聘信息,助你先人一步投递,快人一步入职!京东 AI 研究院京东 AI 研究院(https://air.jd..._京东计算机视觉实验室

Ubuntu18.04安装配置Qt5.15_ubuntu安装qt5.15-程序员宅基地

文章浏览阅读2.1k次。Ubuntu18.04安装配置Qt5.15 Ubuntu18.04安装配置Qt5.15 Qt选择下载Qt安装Qt5.15.0配置后记 Qt选择 在官方的声明中,Qt5.15是Qt5.x的最后一个LTS版本,增加了即将在2020年底推出的Qt6的部分新特性,为了之后的新_ubuntu安装qt5.15

针对Error: You must either define the environment variable DJANGO_SETTINGS_MODULE ...问题的解决_project structure->facets->django->-程序员宅基地

文章浏览阅读1.8w次,点赞5次,收藏3次。针对Error: You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings问题的解决使用intelliJ Idea开发django项目,启动 manage.py 测试时,会出现如上所示问题。根据提示,有两种解_project structure->facets->django->

随便推点

rufus 一款好用的linux u盘,光盘刻录工具_rufus可以刻录光盘吗-程序员宅基地

文章浏览阅读2.2k次。rufus 一款好用的linux u盘,光盘刻录工具:下载(点击普通下载中的“立即下载”): http://share.cnop.net/file/1806028-401886318_rufus可以刻录光盘吗

用VB.net实现对.ini文件的读写操作的类-程序员宅基地

文章浏览阅读142次。Option Explicit OnModule INI 'INICont.bas Ver 1.0+a INI '==================================================================== 'GetIntFromINI( sectionName , keyName , defaultValue, iniPath ) '..._vb.net 读取ini文件 int

linux集群—负载均衡集群LBC_lbc在程序中是什么-程序员宅基地

文章浏览阅读615次。1 集群的定义集群的出现主要是为了解决单台设备性能不足、效率低下等问题,可以保证业务无中断,总体效率高,适合大型业务。2 集群的分类3 负载均衡集群LBC_lbc在程序中是什么

【渝粤题库】陕西师范大学200161 文字学概论 作业_十小羊未哪一个不是象形字-程序员宅基地

文章浏览阅读3k次。《文字学概论》作业一、单选题1、原始社会陶器上的图形符号刻划是以( )为代表的。A 仰韶文化 B 马家窑文化 C 良渚文化 D 大汶口文化2、下列各字中不属于象形字的是( )。A十 B小 C羊 D未3、“画成弃物 ,随体诘屈”,是许慎给六书当中的( )所下的定义。A 象形 B 指事 C 假借 D 会意4、下列各字当中形旁为"肉”的是( )。A 肌 B 明 C 钥 D 腾5、“夫” 字是在“大”字之上加一横而成的,这_十小羊未哪一个不是象形字

tiny_tds: 简易的Microsoft SQL Server驱动程序 for Ruby-程序员宅基地

文章浏览阅读328次,点赞9次,收藏8次。tiny_tds: 简易的Microsoft SQL Server驱动程序 for Rubytiny_tds 是一个小巧且高效的Microsoft SQL Server驱动程序,专为Ruby编程语言设计。它提供了与SQL Server进行交互的能力,可以轻松地执行查询、事务管理以及数据操作。项目简介tiny_tds是一个开源项目,由Rails SQL Server社区开发并维护。该库支持多种...

高性能mysql第五版在线阅读_MySQL零基础入门推荐书籍(收藏版)-程序员宅基地

文章浏览阅读8.6k次。1,基础篇:1.1《SQL基础教程》本书是畅销书《SQL基础教程》第2版,介绍了关系数据库以及用来操作关系数据库的SQL语言的使用方法。书中通过丰富的图示、大量示例程序和详实的操作步骤说明,让读者循序渐进地掌握SQL的基础知识和使用技巧,切实提高编程能力。每章结尾设置有练习题,帮助读者检验对各章内容的理解程度。另外,本书还将重要知识点总结为“法则”,方便读者随时查阅。第2版除了将示例程序更新为对应..._高性能mysql第五版 pdf

推荐文章

热门文章

相关标签