总言

  C语言:文件操作。
  

文章目录

  • 总言
  • 1、文件是什么?为什么需要文件?
    • 1.1、为什么需要文件?
    • 1.2、文件是什么?
  • 2、文件的打开与关闭
    • 2.1、文件指针
    • 2.2、文件打开和关闭:fopen、fclose
    • 2.3、文件使用方式
  • 3、文件的顺序读写
    • 3.1、字符输入输出:fputc、fgetc
      • 3.1.1、fputc
      • 3.1.2、fgetc
    • 3.2、适用于所有流:stream的简单介绍
    • 3.3、文本输入输出:fputs、fgets
      • 3.3.1、fputs
      • 3.3.2、fgets
    • 3.4、格式化输入输出:fscanf、fprintf
      • 3.4.1、fprintf
      • 3.4.2、fscanf
    • 3.5、与字符串相关的输入输出:sscanf、sprintf
      • 3.5.1、对比说明
      • 3.5.2、使用介绍
    • 3.6、二进制输入输出:fread、fwrite
      • 3.6.1、fwrite
      • 3.6.2、fread
  • 4、文件的随机读写
    • 4.1、fseek
    • 4.2、ftell
    • 4.3、rewind
  • 5、其它相关内容
    • 5.1、文本文件和二进制文件
    • 5.2、文件读取结束判定
    • 5.3、文件缓冲区

  

1、文件是什么?为什么需要文件?

1.1、为什么需要文件?

  模拟实现通讯录时,会面临这样一个实际问题:在通讯录中把信息记录下来后,只有在我们自己选择删除数据的情况下,数据才不复存在,否则需要长久保留数据。
  这就涉及到了数据持久化的问题,一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。
  
  

1.2、文件是什么?

  1)、文件分类
  磁盘上的文件是文件。但在程序设计中,从文件功能的角度来看,一般文件有两种:程序文件、数据文件。
  程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
  数据文件: 文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
  
  
  2)、文件名称
  一个文件要有一个唯一的文件标识,以便用户识别和引用。为了方便起见,文件标识常被称为文件名
  文件名包含3部分:文件路径+文件名主干+文件后缀

D:\日常\TIM\Tencent Files\All Users\test.txt
文件路径:D:\日常\TIM\Tencent Files\All Users\
文件名主干:test
文件后缀:.txt

  
  
  

2、文件的打开与关闭

2.1、文件指针

  1)、文件指针是什么?

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

  不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。
  
  
  VS2019下FILE声明:

#ifndef _FILE_DEFINED#define _FILE_DEFINEDtypedef struct _iobuf{void* _Placeholder;} FILE;
#endif

  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;

  

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

  
  2)、文件指针的作用

  我们可以创建一个FILE*的指针变量,如下:

FILE* pf;

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

2.2、文件打开和关闭:fopen、fclose

  1)、fopen、fclose函数介绍
  相关函数链接:fopen

fopen
FILE * fopen ( const char * filename, const char * mode );

  const char * filename:所需要打开的文件名称。
  const char * mode:打开文件的方式,此处类型为字符指针,用于存放字符串首字符的地址。
  FILE *:在内存中创建一个与打开文件有关的文件信息区,同时返回一个文件指针,该指针指向文件信息区的起始地址(结构体)。
  NULL:如果文件打开失败,则返回空指针,同动态内存一致,需要进行判空操作,若错误则显示错误原因,并结束程序。
  
  
  相关链接:fclose

fclose
int fclose ( FILE * stream );

  FILE * stream:向指定要关闭的FILE指针
  
  
  2)、使用举例

int main()
{//打开文件FILE* pf = fopen("test.txt", "r");//“r”此处为字符串,用双引号,r是一种文件读取模式if (pf == NULL){perror("fopen");return 1;}//读文件//……//关闭文件fclose(pf);pf = NULL;return 0;
}

  情形一:

  情形二:

  
  情形三:绝对路径与相对路径说明
  假如要打开的文件不在当前路径下,需要使用对应路径打开:

  
  
  

2.3、文件使用方式

  以下mode会在后续学习中慢慢用到。

  
  
  

3、文件的顺序读写

3.1、字符输入输出:fputc、fgetc

3.1.1、fputc

  相关函数链接:fputc

fputc
int fputc ( int character, FILE * stream );

  将字符character按顺序写入对应的文件流stream中,此处使用的是文件指针。如果失败则返回EOF。

  演示如下:

int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\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;
}

  
  
  

3.1.2、fgetc

  相关函数链接:fgetc

fgetc
int fgetc ( FILE * stream );

  从对应的文件指针指向的文件中按顺序依次读取字符,若遇到文件结尾/错误则返回EOF,若成功则返回字符读取(提升为 int 值)。
  
  使用演示:

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

  
  

3.2、适用于所有流:stream的简单介绍

  我们知道各种各样的外部设备,比如键盘、屏幕、U盘、硬盘、网卡等,数据信息的读写在它们之间进行交换,但不同的设备其读写方式可能存在差异,这样一来使用效率就会大幅降低,因为我们在做数据输入输出的前提是要学习了解它们的原理。因此,为了简化这一过程,我们在这些外设间引入一个中间商,即流,用于输入输出。这样一来,我们在各种数据交换时,就不必关心流与这些外设间是如何做到数据信息交换的,相当于一种封装。

  相关演示:

int main()
{int ch = fgetc(stdin);printf("%c ", ch);fputc(ch, stdout);return 0;
}


  
  
  

3.3、文本输入输出:fputs、fgets

3.3.1、fputs

  相关函数链接:fputs

  相关演示:

int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//读写文件fputs("落霞与孤鹜齐飞,", pf);fputs("秋水共长天一色。\n", pf);fputs("渔舟唱晚,响穷彭蠡之滨;\n", pf);fputs("雁阵惊寒,声断衡阳之浦。\n", pf);//关闭文件fclose(pf);pf = NULL;return 0;
}

  可以看到,没加换行符,fputs会将当前内容放置于上此文尾,若加了换行符则另起一行。

  
  

3.3.2、fgets

  相关函数链接:fgets

  若成功,则返回str指针指向地址,若失败,则返回NULL;
  
  相关演示一:

int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "r");if (pf == NULL){perror("fopen");return 1;}char arr[255];//读写文件fgets(arr, 254, pf);printf("%s\n", arr);while (fgets(arr, 254, pf)!=NULL)printf("%s", arr);//关闭文件fclose(pf);pf = NULL;return 0;
}


  
  
  验证fgets会保留一位将\0放入:

int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "r");if (pf == NULL){perror("fopen");return 1;}char arr[10]="XXXXXXXXXX";//读写文件fgets(arr, 5, pf);printf("%s\n", arr);//关闭文件fclose(pf);pf = NULL;return 0;
}


  
  
  
  

3.4、格式化输入输出:fscanf、fprintf

3.4.1、fprintf

  相关函数链接:printf

  printf是将相关内容打印到标准输出,即屏幕上;
  fprintf适用于任意流,可将相关内容打印到指定的stream中。
  
  相关演示:

struct st
{char name[20];int age;double score;
};int main()
{//创建一个学生数据,要求:将该学生数据的内容存入指定文件中struct st s1 = { "张三",25,98.00 };//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//读写文件fprintf(pf,"%s %d %lf", s1.name, s1.age, s1.score);//关闭文件fclose(pf);pf = NULL;return 0;
}

  
  

3.4.2、fscanf

  相关函数链接:fscanf

  scanf是从标准输入流中读取数据,一般是键盘;
  fscanf适用于任意流,可从指定的流stream中读取数据。
  
  相关演示:

struct st
{char name[20];int age;double score;
};int main()
{//创建一个学生数据,要求:从指定文件中读取相关数据信息填入该结构体中struct st s1 = {0};//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读写文件fscanf(pf, "%s %d %lf", s1.name, &(s1.age), &(s1.score));//注意,name为数组名,本身就是地址,故无需取地址操作符,其它二者读取时与scanf别无差异fprintf(stdout, "%s %d %lf", s1.name, s1.age, s1.score);//关闭文件fclose(pf);pf = NULL;return 0;
}

  
  
  

3.5、与字符串相关的输入输出:sscanf、sprintf

3.5.1、对比说明

  1)、scanf/printf、fscanf/fprintf、sscanf、sprintf对比说明

  scanf:针对标准输入输出流的格式化输入函数,Read formatted data from stdin
  printf:针对标准化输入输出流的格式化输出函数,Print formatted data to stdout

scanf
int scanf ( const char * format, ... );
printf
int printf ( const char * format, ... );

  
  fscanf:针对所有输入流的格式化输入函数,Read formatted data from stream
  fprintf:针对所有输出流的格式化输出函数,Write formatted data to stream

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

  
  sscanf:把一个字符串数据转换为格式化数据,Read formatted data from string
  sprintf:把一个格式化的数据转换为字符串数据,Write formatted data to string

sscanf
int sscanf ( const char * s, const char * format, ...);
sprintf
int sprintf ( char * str, const char * format, ... );

  
  

3.5.2、使用介绍

  相关函数链接:sprintf

  
  相关函数链接:sscanf

  
  使用举例:

struct st
{char name[20];int age;double score;
};int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读写操作struct st s1 = { "李四",23,94.5 };//原数据struct st stof = { 0 };//未赋有效值的结构体:用于存放从字符串中转换过来的格式化数据char ftos[255];//字符数组:用于存放从格式化数据中转换得到的字符串//使用sprintf,将格式化数据转换成字符串sprintf(ftos, "%s %d %lf", s1.name, s1.age, s1.score);printf("ftos数组中:%s\n", ftos);//使用sscanf,将字符串转换为格式化数据sscanf(ftos, "%s %d %lf", stof.name, &(stof.age), &(stof.score));printf("stof结构体中:%s %d %lf\n", stof.name, stof.age, stof.score);//关闭文件fclose(pf);pf = NULL;return 0;
}


  
  
  

3.6、二进制输入输出:fread、fwrite

3.6.1、fwrite

  相关函数链接:fwrite

  注意理解该函数各参数含义,ptr是要写入的元素数组的指针,size是单个此类数据的大小(每个元素的大小),count是要写入这类数据的数目(元素个数),stream为指定输出的流。

fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

  相关演示:

struct st
{char name[20];int age;double score;
};int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "wb");if (pf == NULL){perror("fopen");return 1;}//读写操作struct st s1 = { "李四",23,94.5 };fwrite(&s1, sizeof(struct st), 1, pf);//含义:将s1中的数据输出至pf中,个数为1个,每个大小为sizeof(struct st)//关闭文件fclose(pf);pf = NULL;return 0;
}

  

  
  

3.6.2、fread

  相关函数链接:fread

  
  相关演示:

struct st
{char name[20];int age;double score;
};int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "rb");if (pf == NULL){perror("fopen");return 1;}//读写操作struct st s1 = { 0 };fread(&s1, sizeof(struct st), 1, pf);printf("%s %d %lf\n", s1.name, s1.age, s1.score);//关闭文件fclose(pf);pf = NULL;return 0;
}


  
  
  

4、文件的随机读写

4.1、fseek

  相关函数链接:fseek
  根据文件指针的位置和偏移量,重新定位文件指针。

SEEK_SET Beginning of file
SEEK_CUR    Current position of the file pointer
SEEK_END    End of file *

  
  1)、关于随机位置读取


int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//文件读写////文件中存储的例句:turn lemon into lemonade.for (int i = 0; i < 4; i++){printf("%c", fgetc(pf));//turn}printf("\n");//至此,文件指针指向trun lemon中的空白字符//演示一:以当前文件指针作为参考点fseek(pf, -1, SEEK_CUR);//当前指向turn中的nprintf("文件向左偏移一位后:%c\n", fgetc(pf));//fgetc读取后后挪一位,到空白字符fseek(pf, 1, SEEK_CUR);//当前指向为lemon中的lprintf("文件向右偏移一位后:%c\n", fgetc(pf));//fgetc读取后后挪一位,到eprintf("\n");//演示二:以文件头作为参考点,此时偏移量不能为负数fseek(pf, 3, SEEK_SET);//当前指向trun中的nprintf("文件向右偏移三位后:%c\n", fgetc(pf));printf("再次读取一位:%c\n", fgetc(pf));printf("\n");//演示三:以文件尾作为参考点,此时偏移量不能为正数fseek(pf, -3, SEEK_END);//当前指向为lemonade.中的dprintf("文件向左偏移三位后:%c\n", fgetc(pf));printf("再次读取一位:%c\n", fgetc(pf));printf("\n");////关闭文件fclose(pf);pf = NULL;return 0;
}

  
  
  2)、关于随机位置写入

int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//文件读写//for (char ch = 'a'; ch <='z'; ch++){fputc(ch,pf);}//演示一:以当前文件指针作为参考点fseek(pf, -22, SEEK_CUR);fputc('S', pf);//演示二:以文件头作为参考点,此时偏移量不能为负数fseek(pf, 6, SEEK_SET);fputc('O', pf);//演示三:以文件尾作为参考点,此时偏移量不能为正数fseek(pf, -3, SEEK_END);fputc('S', pf);////关闭文件fclose(pf);pf = NULL;return 0;
}

  
  
  

4.2、ftell

  返回文件指针相对于起始位置的偏移量
  相关函数链接:ftell
  
  代码演示如下:

int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//文件读写//for (char ch = 'a'; ch <='z'; ch++){fputc(ch,pf);}fseek(pf, -22, SEEK_CUR);fputc('S', pf);long int pos = ftell(pf);printf("%ld\n", pos);////关闭文件fclose(pf);pf = NULL;return 0;
}

  
  

4.3、rewind

  相关函数链接:rewind
  让文件指针的位置回到文件的起始位置

  相关演示:

int main()
{//打开文件FILE* pf = fopen("D:\\Daily\\test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//文件读写//for (char ch = 'a'; ch <='z'; ch++){fputc(ch,pf);}fseek(pf, -22, SEEK_CUR);fputc('S', pf);long int pos = ftell(pf);printf("%ld\n", pos);rewind(pf);printf("%ld\n", ftell(pf));////关闭文件fclose(pf);pf = NULL;return 0;
}


  
  
  
  

5、其它相关内容

5.1、文本文件和二进制文件

  1)、数据文件分类
  根据数据的组织形式,数据文件被称为文本文件或者二进制文件
  数据在内存中以二进制的形式存储,如果不加转换直接输出到外存,就是二进制文件。
  如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。

  
  
  2)、数据在内存中的存储方式
  字符型数据一律以ASCII形式存储;
  数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
  
  

  3)、举例演示
  以整数10000为例子:

  
  一个测试代码:
  我们以二进制的形式将正数10000写入文件中

#include <stdio.h>
int main()
{int a = 10000;FILE* pf = fopen("D:\\Daily\\test.txt", "wb");fwrite(&a, 4, 1, pf);//二进制的形式写到文件中fclose(pf);pf = NULL;return 0;
}

  假如用文本文件的形式打开,可以看到是乱码

  现在我们在VS中以二进制的形式打开,需要做一点设置,操作如下:

  
  
  
  

5.2、文件读取结束判定

  1)、关于feof函数说明
  在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。feof是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

  
  
  2)、关于文件读取结束说明
  根据文件类型的区别,判断文件读取结束具有一定区别:

  对文本文件读取是否结束,有两种判断方法:1、判断返回值是否为 EOF;2、判断返回值是否为 NULL 。
  例如:fgetc 函数中,判断文件读取结束是看是否返回 EOF,而fgets判断文件读取结束是看返回值是否为 NULL

  对二进制文件读取是否结束,可通过返回值是否小于实际要读的个数来判断。
  例如:fread判断返回值是否小于实际要读的个数。
  
  
  

5.3、文件缓冲区

  1)、什么是文件缓冲区
  ANSIC 标准采用“缓冲文件系统”处理的数据文件。所谓缓冲文件系统,是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。

  2)、为什么需要文件缓冲区
  文件缓冲区是一个链接内存与硬盘的过渡段,从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。之所以要这样做,是因为内存和硬盘处理数据的速度不一,若直接从磁盘中读取数据输送到内存, 会面临频繁访问、占用内存资源的现象。故而在二者之间搭建出一个中间商。
  需要注意的是,缓冲区的大小是根据C编译的系统来决定。
  
  
  
  
  

【ONE·C || 文件操作】相关推荐

  1. C++核心编程(四)--文件操作

    5 文件操作 程序运行时产生的数据都属于临时数据,程序一点运行结束,就会被释放 通过文件可以将数据持久化 C++中对文件操作需要包含头文件:fstream 文件类型分为两种: 文本文件:文件以文本的A ...

  2. python codecs.open()及文件操作-文本处理 with open

    20210810 以二进制并追加的形式写入文件 20210731 f.write('\n') 换行写入 20210625 https://blog.csdn.net/a543402496/articl ...

  3. day07-字符编码、文件操作

    目录: 字符编码 文件处理 一.字符编码: #1. 什么是字符编码:将人识别的字符转换计算机能识别的01,转换的规则就是字符编码表 #2. 常用的编码表:ascii.unicode.GBK.Shift ...

  4. HTML5 本地文件操作之FileSystemAPI整理(二)

    一.文件目录操作 1.DirectoryEntry对象 属性: 1.isFile: 操作对象的是否为文件,DirectoryEntry对象固定其值为false 2.isDirectory: 操作对象是 ...

  5. python3 转码的函数_python基础3之文件操作、字符编码解码、函数介绍

    内容概要: 一.文件操作 二.字符编码解码 三.函数介绍 一.文件操作 文件操作流程: 打开文件,得到文件句柄并赋值给一个变量 通过句柄对文件进行操作 关闭文件 基本操作: 1 #/usr/bin/e ...

  6. linux文件编辑操作,Linux下文本编辑及其文件操作

    文本编辑及其文件操作 Vim 命令模式: dd 删除当前行 yy 2yy nyy 复制 从光标算起,复制n行 p 粘贴 默认粘贴在光标的下一行 u 撤销 末行模式: :wq 保存退出 :wq! 强制保 ...

  7. c++:文件操作1 文件的打开

    在程序中,要使用一个文件,先要打开文件后才能读写,读写完后要关闭.创建一个新文件也要先执行打开(open)操作,然后才能往文件中写入数据.C++ 文件流类有相应的成员函数来实现打开.读.写.关闭等文件 ...

  8. python写byte数组到文件_这可能是写过最详细的Python文件操作。网友:收藏备用(中篇)...

    很多同学对于编码问题都不是很清楚,计算机常见的编码格式为: ASCII ISO-8859-1 GB2312 GBK UTF-8 UTF-16 对于Python开发中,我们一般都是采用统一的编码格式:U ...

  9. 浅谈无缓存I/O操作和标准I/O文件操作区别 (转载)

    首先,先稍微了解系统调用的概念: 系统调用,英文名system call,每个操作系统都在内核里有一些内建的函数库,这些函数可以用来完成一些系统系统调用把应用程序的请求传给内核,调用相应的的内核函数完 ...

  10. C语言文件操作函数大全

    unix中一切皆文件,所以文件操作至关重要! clearerr(清除文件流的错误旗标) 相关函数 feof 表头文件 #include<stdio.h> 定义函数 void clearer ...

最新文章

  1. 给网站添加icon图标
  2. react draft api 简介
  3. C语言 | C语言中main函数:int main( int argc, char* argv[] ) 中arg和argv参数的解析及调试
  4. 开始做在线拍照功能了
  5. 在Finder标题栏上显示完整路径
  6. LeetCode65——Valid Number(使用DFA)来判断字符串是否为数字
  7. 快手2021服饰品类洞察报告
  8. 2018年技术上该怎样努力
  9. OpenDaylight系列教程(十一)-- Installing OpenDaylight
  10. 【数据结构笔记17】集合的表示、双亲表示法、并查集、集合的并运算
  11. 2017行进中的杂想,做一个极少数的历史
  12. 机器学习之推荐算法分类
  13. cmd跑绿色代码_cmd装逼代码-cmd命令,装逼
  14. ARM嵌入式系统的问题分析与总结
  15. 什么是RS485总线?怎么使用RS485总线?——转自微信公众号弱点课堂
  16. mysql之mysql.sock文件
  17. 分布式对象存储oss-minio
  18. C语言:fflush()的用法以及缓冲区的概念
  19. HIF转16位TIF或者PNG
  20. [生存志] 第71节 阖闾五战入郢

热门文章

  1. java的scope_spring中@Scope作用域的注解
  2. 优链时代喜获第五届“绽放杯”5G应用征集大赛云XR专题赛一等奖!
  3. ZooKeeper 第一节 基本概念
  4. 排障秘籍——mysql主从同步复制报错Errno参数解释
  5. 一文了解Gauss数据库:开发历程、OLTPOLAP特点、行式列式存储,及与Oracle和AWS对比
  6. 警惕冒充“数字人民币”诈骗案!
  7. 随笔1--清河急还是沙河急
  8. #初入门 各类加密方法的判断(1)
  9. ROS机器人项目开发11例-ROS Robotics Projects(5)深度学习
  10. 游戏开发的最佳编程语言及免费引擎推荐