C文件操作
目录
1. 什么是文件
2. 为什么要有文件
3. 文件名
4. 文件类型
5. 文件指针
6. 文件的打开和关闭
7. 文件的顺序读写
7.1. fgetc
7.2. fputc
7.3. fgets
7.4. fputs
7.5. fscanf
7.6. fprintf
7.8. sscanf
7.9. sprintf
7.9. fread
7.10. fwrite
8. 文件的随机读写
8.1. fseek
8.2. ftell
8.3. rewind
9. 文件结束的判定
1. 什么是文件
简单讲,磁盘上的文件是文件。它是一组有序的字节序列,用于保存和组织信息。在计算机系统中,文件可以包含各种类型的数据,例如文本、图像、音频、视频等。文件是计算机中的一个抽象概念,它在硬盘实际以二进制数据的形式存储。
而在程序设计中,我们一般谈的文件分为两种:程序文件、数据文件(我们接下来主要讨论的就是数据文件)
程序文件:包括C源文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件
2. 为什么要有文件
我们知道,内存实际上是一种带电存储的存储介质,也就是说只有通电时,内存才能存储数据。但是事实上,有时候我们也需要不带电的存储方式:文件可以将数据永久保存在计算机的存储介质中,以便于后续使用和处理。在没有文件的情况下,数据只能暂时存储在计算机内存中,一旦计算机断电或重启,数据就会丢失。
3. 文件名
文件名是用来标识和区分不同文件的一串字符或字符串。
文件名包含三部分:文件路径 + 文件名主干 + 文件后缀,例如:E:\vs2013\Code\Code_2023_10\Project_10_31\Project_10_31\test.c
4. 文件类型
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。文本文件是一种存储纯文本数据的文件类型。它包含了由ASCII码或Unicode编码表示的文本字符,用来表示文字的内容,而不包含任何格式、样式或图像等其他元素。
文本文件是由连续的字符构成,每个字符都有一个对应的编码值。常见的编码方式包括ASCII、UTF-8和UTF-16等。其中,ASCII编码适用于英语和其他拉丁字母字符,而UTF-8和UTF-16编码则可以包含更广泛的字符集,包括多种语言的文字、符号和表情等。
常见的文本文件格式包括.txt(纯文本文件)、.csv(逗号分隔值文件,常用于电子表格数据)、.html(超文本标记语言文件,用于网页)、.json(JavaScript对象表示法文件,用于数据交换)、.xml(可扩展标记语言文件,用于数据描述)等。这些文本文件可以通过文本编辑器或特定的软件程序进行打开、编辑和保存。
二进制文件是一种存储任意数据的文件类型,它包含了以二进制形式表示的数据,与文本文件不同,不直接使用可识别的字符集或编码来存储数据。
二进制文件中的数据以字节为单位,在计算机中以二进制数形式表示。这意味着它可以存储任意的数字、文本、图像、音频、视频或其他形式的数据。二进制文件中的数据不遵循特定的结构或格式,而是取决于数据的用途和创建方式。
与文本文件相比,二进制文件的优点是可以存储和处理更广泛的数据类型和格式。例如,图像文件(如.jpg、.png)和音频文件(如.mp3、.wav)通常都是以二进制形式存储的,因为它们包含了大量的非文本数据。
二进制文件通常需要特定的软件程序或应用程序来打开、读取和解析。这些软件程序可以根据特定的文件格式和数据结构来处理和解释数据。例如,图像编辑软件可以解析二进制图像文件并显示图像内容,音频播放器可以解析二进制音频文件并播放音频。
需要注意的是,二进制文件的内容通常不直接可读或可编辑。因为它们没有经过字符编码,所以不能直接以文本形式打开和编辑。如果尝试用文本编辑器打开二进制文件,将看到一堆乱码或不可读的字符。
总之,二进制文件是一种存储任意数据的文件类型,通过以二进制形式存储数据,可以处理各种类型和格式的数据,但需要特定的软件程序来解析和处理。
vs下查看二进制文件:


5. 文件指针
文件指针简单理解就是一个指向文件的指针。其类型固定为FILE*,这个FILE是一个结构体,这个结构体包含了文件各种的相关信息。
6. 文件的打开和关闭
那么如何打开和关闭一个文件呢?C语言为我们提供了一组打开和关闭文件的函数:
// 函数原型
// filename: 文件名
// mode: 打开文件的方式
// return val: 返回一个文件指针,这个指针指向这个被打开的文件
FILE *fopen( const char *filename, const char *mode );// 函数原型
// 关闭一个被打开的文件
// stream: 对应的文件指针
// return val: 如果成功关闭,返回0;失败返回EOF
int fclose( FILE *stream );
打开文件的方式:
| 文件使用方式 | 大概含义 | 如果指定文件不存在 |
| "r" (只读) | 打开一个已经存在的文本文件 | 出错 |
| "w"(只写) | 打开一个文本文件 | 建立一个新文件 |
| "a"(追加) | 向文本文件尾部追加数据 | 建立一个新文件 |
| "rb"(只读) | 打开一个已经存在的二进制文件 | 出错 |
| "wb"(只写) | 打开一个二进制文件 | 建立一个新文件 |
| "ab"(追加) | 向二进制文件追加数据 | 建立一个新文件 |
| "r+"(读写) | 打开一个已经存在的文本文件 | 出错 |
| "w+"(读写) | 打开一个文件 | 建立一个新文件 |
| "a+"(追加) | 向文件尾部追加数据 | 建立一个新文件 |
| "rb+" | 打开一个已经存在的二进制文件 | 出错 |
| "wb+" | 打开一个二进制文件 | 建立一个新文件 |
| "ab+" | 向一个二进制文件尾部追加数据 | 建立一个新文件 |
现在,我们可以尝试打开一个文件和关闭文件
void Test1(void)
{// 以文本写的方式打开文件FILE* fp = fopen("log.txt", "w");// 打开失败返回NULLif (fp == NULL){perror("open failed");exit(-1);}else{// 关闭文件fclose(fp);}
}
7. 文件的顺序读写
对于下面的函数我个人的理解:将操作者当成"内存",被操作者即"对象"是一个流
| 功能 | 函数 | 适用于 |
| 从一个流中读取一个字符 | int fgetc( FILE *stream ); | 所有输入流 |
| 向一个流中写一个字符 | int fputc( int c, FILE *stream ); | 所有输出流 |
| Get a string from a stream | char *fgets( char *string, int n, FILE *stream ); | 所有输入流 |
| Write a string to a stream | int fputs( const char *string, FILE *stream ); | 所有输出流 |
| Read formatted(格式化) data from a stream | int fscanf( FILE *stream, const char *format [, argument ]... ); | 所有输入流 |
| Print formatted data to a stream | int fprintf( FILE *stream, const char *format [, argument ]...); | 所有输出流 |
| Reads data from a stream | size_t fread( void *buffer, size_t size, size_t count, FILE *stream ); | 文件 |
| Writes data to a stream. | size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream ); | 文件 |
下来我们对上面的函数一一演示一下:
7.1. fgetc
// Read a character from a stream or stdin;
// 读取失败会返回EOF(end of file)
// success: 返回读取的字符
int fgetc( FILE *stream );
void Test2(void)
{// 以文本读打开一个文件FILE* fp = fopen("Test.c", "r");assert(fp);// 如果没有遇到EOF,就继续读while (!feof(fp)){//每次读取一个字符,并向标准输出写入printf("%c", fgetc(fp));}// 关闭一个文件fclose(fp);
}
7.2. fputc
Writes a character to a stream or to stdout.
// ch: 你要写入的字符
// stream: 你要向哪个流写入
// success: return ch
// failed: return EOF
int fputc( int c, FILE *stream );
void Test3(void)
{char ch = 'a';while (ch <= 'z'){// 向标准输出(stdout)写入chfputc(ch, stdout);++ch;}
}
7.3. fgets
//Get a string from a stream.// string: 数据的存储位置
// n: 每次最大读取字符的个数
// stream: 从那个流中读?
// success: 返回读取字符串的起始位置
// failed: 返回NULL
char *fgets( char *string, int n, FILE *stream );
#define BUFFER_MAX 64
void Test4(void)
{char str[BUFFER_MAX] = { 0 };FILE* fp = fopen("Test.c", "r");assert(fp);while (fgets(str, BUFFER_MAX, fp) != NULL){printf("%s", str);}fclose(fp);
}
7.4. fputs
// Write a string to a stream.
// string 你要写入的字符串的起始地址
// stream 你要想哪个流写入
// success: 返回一个!负值
// failed: 返回EOF
int fputs( const char *string, FILE *stream );
void Test5(void)
{const char* str = "hehe\n";for (size_t i = 0; i < 5; ++i){// 向标准输出写入特定字符串fputs(str, stdout);}
}
7.5. fscanf
//Read formatted data from a stream.
// 从特定流(所有输入流)读取格式化数据,与scanf十分类似
// 只不过scanf是固定的从标准输入流读取数据
int fscanf( FILE *stream, const char *format [, argument ]... );
#define BUFFER_MAX 64
void Test6(void)
{FILE* fp = fopen("log.txt1", "r");assert(fp);char str[BUFFER_MAX] = { 0 };while (!feof(fp)){fscanf(fp, "%s", str);printf("%s\n", str);}fclose(fp);
}
7.6. fprintf
// Print formatted data to a stream.
// 向特定流(所有输出流)写入格式化数据
// 与printf十分类似,不过printf是默认向标准输出流写入格式化数据
// success: 返回已写入的字节数
// failed: 返回一个负值
int fprintf( FILE *stream, const char *format [, argument ]...);
void Test7(void)
{const char* str = "haha\n";int* ptr = (int*)malloc(sizeof(int)* 5);for (size_t i = 0; i < 5; ++i){// 其返回值代表每次写入的字节数// 向标准输出流写入特定字符串ptr[i] = fprintf(stdout, "%s", str);}for (int i = 0; i < 5; ++i){printf("%d ", *(ptr + i));}free(ptr);ptr = NULL;
}
7.8. sscanf
// Read formatted data from a string// buffer: 存储数据的起始地址
// sscanf 从一个字符串提取出格式化的数据
// failed: return EOF
int sscanf( const char *buffer, const char *format [, argument ] ... );
typedef struct PeoInfo
{char name[15];int age;
}Info;void Test8(void)
{const char* buffer = "cuihua 18";Info tmp = { 0 };// 从buffer这个字符串提取成格式化的数据sscanf(buffer, "%s%d", tmp.name, &tmp.age);printf("%s %d\n", tmp.name,tmp.age);
}
7.9. sprintf
// Write formatted data to a string.// buffer: 存储将格式化的数据转为stirng的结果
// sprintf 就是将格式化的数据转化为一个字符串int sprintf( char *buffer, const char *format [, argument] ... );
void Test9(void)
{char buffer[BUFFER_MAX] = { 0 };Info tmp = { "cuihua", 18 };// 将格式化数据转化为一个字符串sprintf(buffer, "%s %d\n", tmp.name, tmp.age);printf("%s", buffer);
}
7.9. fread
// Reads data from a stream.
// buffer: 缓冲区
// size: 每次读取的字节数
// count: 要读取多少次
// stream: 从特定流读取size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
void Test10(void)
{char str[BUFFER_MAX] = { 0 };FILE* fp = fopen("Test.c", "r"); assert(fp);// 从fp这个流中读取BUFFER_MAX-1个有效字符fread(str, BUFFER_MAX - 1, 1, fp);printf("%s", str);fclose(fp);
}
7.10. fwrite
//Writes data to a stream.// buffer: 指向被写入的数据
// size: 每次写多少个字节
// count: 写多少次
// stream: 向哪个流写入
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
void Test11(void)
{const char* str = "hehe\n";for (size_t i = 0; i < 5; ++i){// 向标准输出流写入str这个指针指向的内容,每次写size个字节fwrite(str, strlen(str), 1, stdout);}
}
8. 文件的随机读写
8.1. fseek
//Moves the file pointer to a specified location.// fseek 可以根据指针的位置 和 偏移量(offset) 来定位文件指针
// origin 初始位置
// SEEK_CUR 文件指针当前的位置
// SEEK_END 文件末尾的位置
// SEEK_SET 文件开始的位置
int fseek( FILE *stream, long offset, int origin );
void Test12(void)
{const char* str = "abcdefg";FILE* fp = fopen("data.txt", "w");assert(fp);fwrite(str, strlen(str), 1, fp); // 此时data.txt的内容就是 abcdeffclose(fp);FILE* fp1 = fopen("data.txt", "r"); char ch = 0;// 如果我按照顺序读取,那么第一次读必然是ach = fgetc(fp1);printf("%c\n", ch); // 必定是a// 那如果我此时像读取d呢// 由于此时的文件指针fp指向了b,那么 + 2就到了d// 故 offset = 2fseek(fp1, 2, SEEK_CUR);ch = fgetc(fp1);printf("%c\n", ch); // d// 那如果我此时像读取最后一个字符呢,也就是g,如何读取呢?// SEEK_END 文件的末尾fseek(fp1, -1, SEEK_END);ch = fgetc(fp1);printf("%c\n", ch); // gfclose(fp1);
}
8.2. ftell
// Gets the current position of a file pointer.
// ftell函数可以返回当前文件指针相对于起始位置的偏移量
long ftell( FILE *stream );
void Test13(void)
{FILE* fp = fopen("data.txt", "r");// 这个文件的数据: abcdefgassert(fp);fseek(fp, -1, SEEK_END); // 此时这个文件指针指向gprintf("%d\n", ftell(fp)); // 此时就是6fclose(fp);
}
8.3. rewind
//Repositions the file pointer to the beginning of a file.
// 让当前文件指针回到文件的起始位置
void rewind( FILE *stream );
void Test14(void)
{FILE* fp = fopen("data.txt", "r");// 这个文件的数据: abcdefgassert(fp);fseek(fp, -1, SEEK_END); // 此时这个文件指针指向gprintf("%d\n", ftell(fp)); // 此时就是6rewind(fp); // 让这个文件指针回到文件的起始位置printf("%d\n", ftell(fp)); // 此时就是0fclose(fp);
}
9. 文件结束的判定
// Tests for end-of-file on a stream.
// 测试文件是如何结束的
// 如果遇到了EOF(即文件指针到了文件末尾),return !0
// otherwise, return 0int feof( FILE *stream );
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。而是应用于当文件读取结束的时候,feof判断是读取失败结束,还是遇到文件尾结束
相关文章:
C文件操作
目录 1. 什么是文件 2. 为什么要有文件 3. 文件名 4. 文件类型 5. 文件指针 6. 文件的打开和关闭 7. 文件的顺序读写 7.1. fgetc 7.2. fputc 7.3. fgets 7.4. fputs 7.5. fscanf 7.6. fprintf 7.8. sscanf 7.9. sprintf 7.9. fread 7.10. fwrite 8. 文件的随…...
drawio特性
drawio的特性 drawio是领先的基于Web技术的草图和图表功能功能的应用。 保证数据的安全 集成了各种不同的平台,和提供了在线的免费编辑器,可以使用app.diagrams.net来方案,drawio本身不会存储用户的数据。 随着互联网时代的发展࿰…...
LLM-Embedder
1. 目标 训出一个统一的embedding模型LLM-Embedder,旨在全面支持LLM在各种场景中的检索增强 2. 模型的四个关键检索能力 knowledge:解决knowledge-intensive任务memory:解决long-context modelingexample:解决in-context learn…...
xsync 集群远程同步脚本
xsync 集群分发 脚本 (1)需求:循环复制文件到所有节点的相同目录下 (2)需求分析: (a)rsync 命令原始拷贝: rsync -av /opt/module roothadoop103:/opt/(b&am…...
30秒get视频号视频如何下载,保存视频号视频到本地方法!
终于可以告别无法下载视频号视频的烦恼啦!下面是一些只需 30 秒就能get到的t视频号视频如何下载方法,让我们一起来探索如何保存视频号视频到本地方法吧! 首先,要记得这些方法仅适用于个人观看或学习使用,不可用于商业用…...
优化改进YOLOv5算法:加入SPD-Conv模块,让小目标无处遁形——(超详细)
1 SPD-Conv模块 论文:https://arxiv.org/pdf/2208.03641v1.pdf 摘要:卷积神经网络(CNNs)在计算即使觉任务中如图像分类和目标检测等取得了显著的成功。然而,当图像分辨率较低或物体较小时,它们的性能会灾难性下降。这是由于现有CNN常见的设计体系结构中有缺陷,即使用卷积…...
【数据结构】搜索树 与 Java集合框架中的Set,Map
作者主页:paper jie_博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力…...
掌握组件缓存:解开Vue.js中<keep-alive>的奥秘
🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…...
Ajax学习笔记第5天
无论做什么,都请记得那是为自己而做,那就毫无怨言! 【1. 跨域】 1.什么是跨域 跨域是指浏览器不能执行其他网站的脚本。它是浏览器同源策略造成的,是浏览器对JS实施的安全限制。 2.常见的跨域场景 3.什么事同源策略 ÿ…...
20.1 OpenSSL 字符BASE64压缩算法
OpenSSL 是一种开源的加密库,提供了一组用于加密和解密数据、验证数字证书以及实现各种安全协议的函数和工具。它可以用于创建和管理公钥和私钥、数字证书和其他安全凭据,还支持SSL/TLS、SSH、S/MIME、PKCS等常见的加密协议和标准。 OpenSSL 的功能非常…...
Panda3d 教程
Panda3d 教程 偶然之余看到了 Panda3d 这个3D引擎,觉得代码开源然后又比较轻量级,感觉还是比较好上手的,因此就想去学习一下,然后把学习过程记录下来。 网上也都找了不少关于Panda3d 方面的教程,但是感觉都不是很好&a…...
除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂…...
干洗店小程序上门洗鞋店管理软件功能介绍;
干洗店小程序上门洗鞋店管理软件功能介绍; 营销工具-洗鞋店管理软件多渠道玩法,拓客留客 支付-会员管理系统多种支付方式,灵活经营 提供洗鞋店管理软件服务,实现会员精细化运营 会员档案-洗鞋店管理软件记录会员的全方位信…...
【C语言初学者周冲刺计划】1.1用筛选法求100之内的素数
目录 1解题思路: 2代码如下: 3运行代码如图所示: 4总结: (前言周冲刺计划:周一一个习题实操,依次类推加一,望各位读者可以独自实践敲代码) 1解题思路: 首先了解筛选法定义:先把…...
1.Vue—简介、实例与容器、MVVM模型
文章目录 一、Vue简介1.1 特点1.2 搭建Vue开发环境1.2.1 开发版1.2.2 生产版 1.3 下载Vue开发工具1.3.1 GitHub方式1.3.2 国内方式 1.4 消除环境提示 二、 入门程序2.1 HelloWord2.2 分析Hello案例2.3.1 多容器对一实例2.3.2 多实例对应一容器2.3.3 总结 三、MVVM模型 一、Vue简…...
【Java笔试强训】Day7(WY22 Fibonacci数列、CM46 合法括号序列判断)
Fibonacci数列 链接:Fibonacci数列 题目: Fibonacci数列是这样定义的: F[0] 0 F[1] 1 for each i ≥ 2: F[i] F[i-1] F[i-2] 因此,Fibonacci数列就形如:0, 1, 1, 2, 3, 5, 8, 13, …,在Fibonacci数列…...
Linux进程的概念
一:冯诺依曼体系结构 什么叫做体系结构??? 计算机组成 / 芯片架构 输入单元:键盘、话筒、摄像头、usb、鼠标、磁盘(ROM)/ssd、网卡、显卡 存储器:内存(RAM)…...
XML教学视频(黑马程序员精讲 XML 知识!)笔记
第一章XML概述 1.1认识XML XML数据格式: 不是html但又和html有点相似 XML数据格式最主要的功能就是数据传输(一个服务器到另一个服务器,一个网站到另一个网站)配置文件、储存数据当做小型数据可使用、规范数据格式让数据具有结…...
自定义组件实现v-model
要使自定义的Vue组件支持v-model,需要实现一个名为value的prop和一个名为input的事件。在组件内部,将value prop 绑定到组件的内部状态,然后在对内部状态进行修改时触发input事件。 自定义UI组件 <template><input :value"va…...
【自动驾驶】Free space与Ray casting
文章目录 1 Free space是什么2 Ray casting是什么3 它俩啥关系4 TODO 1 Free space是什么 在自动驾驶领域,free space即可行驶区域,在结构化道路的十字路口/非结构化道路都有很大作用。 2 Ray casting是什么 ray casting是计算机视觉领域,…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
