当前位置: 首页 > article >正文

通讯录实现(Linux+Cpp)

通讯录实现(Linux+Cpp)

产品底层思考:

  1. 人员如何存储 -> 链表 (增删改 但是排序不适合)

  2. 文件存储 -> 人员数据的格式 name:xxx,phone:xxx

  3. 人员信息 -> 姓名、电话 引出2
    name: xxx,phone: xxx,age: xxx,addr: xxx
    name: yyy,phone: yyy,age: yyy,addr: yyy

实现通讯录

person结构体

人的姓名 人的手机号

//结构体人包含联系人信息
struct person{char name[NAME_LENGTH];char phone[PHONE_LENGTH];struct person* next;struct person* prev;
};

通讯录结构体

链表联系人

联系人的数量

//通讯录结构
struct contacts{struct person* people;int count;
};

系统架构图

在这里插入图片描述

支持层(实现对数据底层操作)

使用宏定义函数 :

函数使用stack(内存)保存形参 宏使用寄存器保存形参 宏更快!!!

错误示范
#define EXCH(x,y){int tem = x;x = y;y = tem;
}

续航符

#define EXCH(x,y){\int tem = x;\x = y;\y = tem;\
}

续完行后宏定义可以使用
但是由于中间出现分号 有分号导致 if else无法链接
所以为了摆脱分号分开影响 使用do while(0);即可 do while(0);需要加分号!正好与函数;形式满足
do while(0)

核心代码
#define EXCH(x,y) do{\  //续航符int tem = x;\x = y;\y = tem;\
}while(0)
使用后代码
#include<stdio.h>
#define EXCH(x,y) do{\int tem = x;\x = y;\y = tem;\
}while(0)int main()
{int x = 10,y = 20;if(x<y)EXCH(x,y);elseprintf("x大于等于y\n");return 0;
}

头插入

#define LIST_INSERT(item,list) do{\item->next = list;\item->prev = NULL;\list->next->prev = item;\list = item;\
}while(0)

在这里插入图片描述

删除

#define LIST_REMOVE(item,list) do{\if(item->prev != NULL) item->prev->next = item->next;\if(item->next != NULL) item->next->prev = item->prev;\if(list == item) list = item->next;\item->prev = NULL;\item->next = NULL;\
}

在这里插入图片描述

接口层(实现逻辑)

// define interface
//插入到链表的逻辑
//(*ppeople):先*再用  *ppeople:是二级指针操作  对于*的优先级最低!
int person_insert(struct person** ppeople,struct person* ps)
{if(ps == NULL) return -1;LIST_INSERT(ps,(*ppeople)); //*ppeople 变成了头指针值return 0; //成功
}

int person_delete(struct person **ppeople,struct person* ps)
{if(ps == NULL) return -1;//双向链表直接删除LIST_REMOVE(ps,*ppeople);return 0; //成功
}

//通过名字 查找
struct person*  person_search(struct person* people,const char* name)
{struct person* item;for(item = people; item != NULL;item = item->next){if(!strcmp(name,item->name)) break; //查找到联系人 字符串比较使用strcmp 不能直接使用 == }return item;
}

遍历

int person_traversal(struct person* people)
{struct person* item ;for(item = people;item != NULL;item = item->next){// 不建议直接使用printf 对于系统的接口使用定义宏方式 与接口一样但是命名不一样// printf("name: %s phone: %s",item->name,item->phone);INFO("name: %s phone: %s\n",item->name,item->phone);}
}

业务层(根据客户需要使用接口层)

//插入 person_insert(struct person** ppeople,struct person* ps)
int insert_entry(struct contacts *cts)
{//判断传入参数通讯录if(cts == NULL) return -1;//初始化插入对象struct person* item = (struct person*)malloc(sizeof(struct person));//分配空间失败if(item ==  NULL) return -2;//获取姓名 手机号//写到 item->phone 所指向的内存 item->phone天生就是地址INFO("Please Input Name\n");scanf("%s",item->name);INFO("Please Input Phone\n");scanf("%s",item->phone);//插入到链表int ret = person_insert(&cts->people,item);if(0 != ret) // 0 != 1 1的时候失败{free(item); //释放内存return -3; //业务失败返回}    //插入成功 人数 +1cts->count++;INFO("插入成功\n");}

打印

int print_entry(struct contacts* cts)
{if(cts == NULL) return -1;//打印 person_traversal(struct person* people)person_traversal(cts->people);}

删除

int delete_entry(struct contacts* cts)
{if(cts == NULL) return -1;//找到 name联系人INFO("Please Input Delete Name\n");char name[NAME_LENGTH] = {0};scanf("%s",name);struct person* ps = person_search(cts->people,name);//找到了if(NULL == ps){INFO("Person don't Exist\n");return -2;  }//如果有person 删除 person_delete(&cts->people,ps);free(ps);cts->count--;INFO("Delete Success\n");return 0;//删除该person
}

查找

int search_entry(struct contacts* cts)
{if(cts == NULL) return -1;INFO("Please Input Search Name\n");char name[NAME_LENGTH] = {0};scanf("%s",name);struct person* ps = person_search(cts->people,name);if(NULL == ps)  //null == ps 是否查到{INFO("Person don't Exist\n");return -2;}//成功找到输出INFO("name: %s,phone: %s\n",ps->name,ps->phone);}

主函数 选择模式

  1. 打印菜单

  2. 选择模式

  3. 选择模式

    1. 为了增强代码可解释性使用枚举 实际含义与数字一一对应

枚举类选择可解释性增强

//枚举类将含义与数字对应 OPER:操作 第一个为1 接下来自动顺序补
enum{OPER_INSERT = 1,OPER_PRINT,OPER_DELETE,OPER_SEARCH,OPER_SAVE,OPER_LOAD // 最后一个没有都好
};

主函数

int main()
{//初始化通讯录内存struct contacts *Contacts = (struct contacts*)malloc(sizeof(struct contacts));//目标内存全部补0memset(Contacts,0,sizeof(Contacts));while(1){int select = 0;menu_info();scanf("%d",&select);switch(select){case OPER_INSERT:insert_entry(Contacts);break;case OPER_PRINT:print_entry(Contacts);break;case OPER_DELETE:delete_entry(Contacts);break; case OPER_SEARCH:search_entry(Contacts);break; case OPER_SAVE:break; case OPER_LOAD:break;     }}return 0;
}

系统架构图更新

image-20250604130133514

支持层

保存数据 内存->磁盘

  1. 判断传入数据
  2. 遍历数据
  3. 保存到缓冲区
  4. 加载到磁盘
int save_file(struct person* people,char* filename)
{if(people == NULL) return -1;FILE* fp = fopen(filename,"w");//1.遍历数据struct person* item = NULL;for(item = people;item != NULL;item = item->next){//2.保存到缓冲区fprintf(fp,"name: %s,phone: %s\n",item->name,item->phone);//3.加载到磁盘fflush(fp);}//4.关闭文件中介fclose(fp);}

加载数据 磁盘->内存

  1. 判断参数
  2. 打开文件中介
  3. 按行读取到缓冲区
  4. 解析一行数据获得name phone关键字
  5. 关闭文件中介

int load_file(struct person **people,int *count,char* filename)
{//1.判断参数if(people == NULL) return -1;//2.打开文件中介 加载中介数据到缓冲区FILE* fp = fopen(filename,"r");while(0 == feof(fp)) //0为未到文件尾{char buffer[BUFFER_LENGTH] = {0};fgets(buffer,BUFFER_LENGTH,fp); //name: tom,phone: 111111111111//3.解析数据得到name phone值char name[NAME_LENGTH] = {0};char phone[PHONE_LENGTH] = {0};if( 0 != parser_token(buffer,BUFFER_LENGTH,name,phone))continue; //解析失败//4.插入通讯录// insert_entry():客户插入 不是底层插入struct person* ps = (struct person*)malloc(sizeof(struct person));if(ps == NULL) return -2; //未识别到空间// strcpy(ps->name,name); 字符串赋值memcpy(ps->name,name,NAME_LENGTH);memcpy(ps->phone,phone,PHONE_LENGTH);person_insert(people,ps);(*count)++;//通讯录数量+1}//5.关闭文件中介fclose(fp);
}
bug1:16行 BUFFER_LENGTH

传入length 为 BUFFER_LENGTH时,length过长 在解析函数中i<length 为phone赋值 phone会越界赋值

int load_file(struct person **people,int *count,char* filename)
{//1.判断参数//2.打开文件中介 加载中介数据到缓冲区FILE* fp = fopen(filename,"r");if(fp == NULL) return -1;while(0 == feof(fp)) //0为未到文件尾{char buffer[BUFFER_LENGTH] = {0};fgets(buffer,BUFFER_LENGTH,fp); //name: tom,phone: 111111111111//3.解析数据得到name phone值char name[NAME_LENGTH] = {0};char phone[PHONE_LENGTH] = {0};int length = strlen(buffer);if( 0 != parser_token(buffer,length,name,phone))continue; //解析失败//4.插入通讯录// insert_entry():客户插入 不是底层插入struct person* ps = (struct person*)malloc(sizeof(struct person));if(ps == NULL) return -2; //未识别到空间// strcpy(ps->name,name); 字符串赋值memcpy(ps->name,name,NAME_LENGTH);memcpy(ps->phone,phone,PHONE_LENGTH);person_insert(people,ps);(*count)++;//通讯录数量+1}//5.关闭文件中介fclose(fp);
}

bug2: length可能为0 导致解析时buffer[0]数组访问越界 Segmentation fault (core dumped)

设置buffer最小长度 防止越界访问

//length
int parser_token(char* buffer,int length,char* name,char* phone)
{if(buffer == NULL) return -1;if(length < MIN_TOKEN_LENGTH) return -2;//对buffer中数据 分开 使用状态机得到value //name: tom,phone: 111111111111int status = 0;int i = 0,j = 0;for(i = 0;buffer[i] != ',';i++) //对buffer中数据 分开遍历{if(status == 0){if(buffer[i] == ' '){status = 1;}}else if(status == 1) //保存value{name[j++] = buffer[i];}}j = 0;status=0;for(;i<length;i++){if(status == 0){if(buffer[i] == ' '){status = 1;}}else if(status == 1){phone[j++] = buffer[i];}}return 0;
}

bug3:在写入时将\n也写入了

组件式开发/产品化开发

状态机解析一行数据

首先

分解序列 ,前后分解序列

明确有几个状态 两个 key value

在某状态对应什么情况下 发生了各种动作 0 + 空格 =>1 0+非空格=》0 1+空格+》不存在 1+非空格=》保存字符串

状态如何改变 在某状态下我们为实现目的会怎么做 1+非空格=》保存字符 某状态实现目的

image-20250604134429328

int parser_token(char* buffer,int length,char* name,char* phone)
{if(buffer == NULL) return -1;//对buffer中数据 分开 使用状态机得到value //name: tom,phone: 111111111111int status = 0;int i = 0,j = 0;for(i = 0;buffer[i] != ',';i++) //对buffer中数据 分开遍历{if(status == 0){if(buffer[i] == ' '){status = 1;}}else if(status == 1) //保存value{name[j++] = buffer[i];}}j = 0;status=0;for(;i<length;i++){if(status == 0){if(buffer[i] == ' '){status = 1;}}else if(status == 1){phone[j++] = buffer[i];}}return 0;
}

学习:

fprintf
feof
fgets
fopen
fflush
  1. 业务层传入通讯录(业务) 接口层传入其他的联系人链表(接口)

9.文件保存加载业务实现

保存磁盘 业务接受cts 以及实现用户要输入的参数 接口层接受people 操作数据

  1. 判断参数
  2. 输入文件名
  3. 调用save_file(people,filename) 保存到本地
//业务逻辑用户输入filename而不是传入filename    char* filename:
int save_entry(struct contacts* cts)
{if(cts == NULL) return -1;//0.提示输入保存的文件名char filename[FILENAME_LENGTH] = {0};INFO("Please Input Saving Filename\n");scanf("%s",filename);//1.调用保存文件保存//cts->people 保存数据哪里来   filename:保存文件的文件名  save_file(cts->people,filename);
}

加载到内存 业务接受cts 以及实现用户要输入的参数 接口层接受people 操作数据

  1. 判断参数
  2. 输入文件名从改文件加载
  3. 调用load_file(&people,filename)

//加载文件到内存
int load_entry(struct contacts* cts)
{//1.参数判断if(cts == NULL) return -1;//2.提示输入要加载文件char filename[FILENAME_LENGTH] = {0};INFO("Please Input Loading Filename\n");scanf("%s",filename);//3.调用加载接口函数load_file(&(cts->people),&(cts->count),filename);}

主函数使用


int main()
{//初始化通讯录内存struct contacts *Contacts = (struct contacts*)malloc(sizeof(struct contacts));//目标内存全部补0memset(Contacts,0,sizeof(Contacts));while(1){int select = 0;menu_info();scanf("%d",&select);switch(select){case OPER_INSERT:insert_entry(Contacts);break;case OPER_PRINT:print_entry(Contacts);break;case OPER_DELETE:delete_entry(Contacts);break; case OPER_SEARCH:search_entry(Contacts);break; case OPER_SAVE:save_entry(Contacts);break; case OPER_LOAD:load_entry(Contacts);break;     default:goto exit;}}
exit:free(Contacts);return 0;
}

退出进程

default:goto exit;
exit:free(Contacts);return 0;

相关文章:

通讯录实现(Linux+Cpp)

通讯录实现&#xff08;LinuxCpp&#xff09; 产品底层思考&#xff1a; 人员如何存储 -> 链表 &#xff08;增删改 但是排序不适合&#xff09; 文件存储 -> 人员数据的格式 name:xxx,phone:xxx 人员信息 -> 姓名、电话 引出2 name: xxx,phone: xxx,age: xxx,addr…...

K8S主机漏洞扫描时检测到kube-服务目标SSL证书已过期漏洞的一种永久性修复方法

1、背景 PaaS平台102xx、102xx端口检测到目标SSL证书已过期漏洞&#xff0c;分别对应kube-controller-manager证书、kube-scheduler证书。 2、系统版本 1.0、2.0版本均涉及。 k8s 1.19、1.23版本均涉及。 3、故障现象 PaaS平台部署1年以后&#xff0c;在主机漏洞扫描时&a…...

质检 LIMS 系统数据防护指南 三级等保认证与金融级加密方案设计

面对频发的数据泄露事件&#xff0c;企业亟需构建一套 “防得住、追得回、打得赢” 的防护体系。质检 LIMS 系统通过三级等保认证与金融级加密的结合&#xff0c;为这一目标提供了可行路径。 一、金融级加密&#xff1a;构建数据防护的 “铜墙铁壁” 金融级加密技术通过协议加密…...

Spring Boot 从Socket 到Netty网络编程(上):SOCKET 基本开发(BIO)与改进(NIO)

前言 无论是软件还是硬件的本质都是要解决IO问题&#xff08;输入、输出&#xff09;&#xff0c;再说回网络编程本质上都是基于TCP/UP的开发&#xff0c;socket是在此基础上做的扩展与封装&#xff0c;而Netty又是对socket做的封装。本文旨在通过相关案例对socket进行探讨。 一…...

79. Word Search

题目描述 79. Word Search 回溯 代码一&#xff0c;使用used数组 class Solution {vector<pair<int,int>> directions{{0,1},{0,-1},{1,0},{-1,0}};vector<vector<bool>> used; public:bool exist(vector<vector<char>>& board, st…...

结构性设计模式之Facade(外观)设计模式

结构性设计模式之Facade&#xff08;外观&#xff09;设计模式 前言&#xff1a; 外观模式&#xff1a;用自己的话理解就是用户看到是一个总体页面&#xff0c;比如xx报名系统页面。里面有历年真题模块、报名模块、教程模块、首页模块… 做了一个各个模块的合并&#xff0c;对…...

ICML 2025 Spotlight | 机器人界的「Sora」!让机器人实时进行未来预测和动作执行!

标题&#xff1a;Video Prediction Policy: A Generalist Robot Policy with Predictive Visual Representations 作者&#xff1a;Yucheng Hu, Yanjiang Guo, Pengchao Wang, Xiaoyu Chen, Yen-Jen Wang, Jianke Zhang, Koushil Sreenath, Chaochao Lu, Jianyu Chen 机构&am…...

CSP严格模式返回不存在的爬虫相关文件

文章目录 说明示例&#xff08;返回404&#xff09;示例&#xff08;创建CSP例外&#xff09; 说明 日期&#xff1a;2025年6月4日。 CSP严格模式是default-src none&#xff0c;但有些web应用中&#xff0c;在爬虫相关文件不存在的情况下&#xff0c;依旧返回了对应文件&…...

https(SSL)证书危机和可行的解决方案

证书危机 20250411,CAB 论坛服务器证书工作组&#xff08; SCWG &#xff09;投票通过一项重大提案《 SC-081v3: 引入缩短有效期和数据重复使用期的时间表》&#xff0c;最终决定&#xff1a;从 2026 年起 SSL/TLS 证书的最大有效期将从 398 天逐步缩短至 47 天&#xff0c;并…...

C#获取磁盘容量:代码实现与应用场景解析

C#获取磁盘容量&#xff1a;代码实现与应用场景解析 在软件开发过程中&#xff0c;尤其是涉及文件存储、数据备份等功能时&#xff0c;获取磁盘容量信息是常见的需求。通过获取磁盘的可用空间和总大小&#xff0c;程序可以更好地进行资源管理、预警提示等操作。在 C# 语言中&a…...

2359. 找到离给定两个节点最近的节点

2359. 找到离给定两个节点最近的节点 题目链接&#xff1a;2359. 找到离给定两个节点最近的节点 代码如下&#xff1a; class Solution { public:int closestMeetingNode(vector<int>& edges, int node1, int node2) {auto calc_dis [&](int x) {vector<in…...

前端导入Excel表格

前端如何在 Vue 3 中导入 Excel 文件&#xff08;.xls 和 .xlsx&#xff09;&#xff1f; 在日常开发中&#xff0c;我们经常需要处理 Excel 文件&#xff0c;比如导入数据表格、分析数据等。文章将在 Vue 3 中实现导入 .xls 和 .xlsx 格式的文件&#xff0c;并解析其中的数据…...

AI生态警报:MCP协议风险与应对指南(下)——MCP Host安全

AI生态警报&#xff1a;MCP协议风险与应对指南&#xff08;上&#xff09;——架构与供应链风险https://blog.csdn.net/WangsuSecurity/article/details/148335401?sharetypeblogdetail&sharerId148335401&sharereferPC&sharesourceWangsuSecurity&spm1011.24…...

基于VLC的Unity视频播放器(四)

上篇文章中提到的问题 播放某个m3u8地址时会嘎掉&#xff0c;想办法解决了一下&#xff0c;很粗暴的&#xff0c;先SetFormat&#xff0c;再Stop&#xff0c;最后再Play&#xff0c;能用…… if (player ! null && player.GetSize() 0) {player.GetSize((w, h) >…...

pixel刷入Android15 userdebug版本

最近入手一个pixel7,想着刷个userdebug版本&#xff0c;就不用模拟器调试开发了&#xff0c;结果按照网上的教程&#xff0c;每次刷机后都是卡在goole logo界面&#xff0c;卡了一天多我才找到问题所在&#xff0c;想着记录下&#xff0c;给自己做个备份。 1. 前期准备&#x…...

【Go-补充】ioReader + ioWriter + bufio

一、io.Reader 和 io.Writer 接口介绍 在 Go 语言中&#xff0c;io 包定义了两个最基础和最重要的接口&#xff1a;io.Reader 和 io.Writer。它们是 Go 语言中进行输入/输出操作的核心抽象&#xff0c;实现了极大的灵活性和可组合性。 io.Reader 接口 io.Reader 接口定义了一…...

leetcode 3403. 从盒子中找出字典序最大的字符串 I 中等

给你一个字符串 word 和一个整数 numFriends。 Alice 正在为她的 numFriends 位朋友组织一个游戏。游戏分为多个回合&#xff0c;在每一回合中&#xff1a; word 被分割成 numFriends 个 非空 字符串&#xff0c;且该分割方式与之前的任意回合所采用的都 不完全相同 。所有分…...

C# 一个解决方案放一个dll项目,一个dll测试项目 ,调试dll项目的源码

一个解决方案&#xff08;sln&#xff09;中放入2个项目(project&#xff0c;通常是一个文件夹)&#xff0c;一个dll项目&#xff0c;一个dll测试项目 右键dll测试项目&#xff0c;设为启动项目。 在dll测试项目添加引用 1&#xff09;右键测试项目 → 添加 → 引用 → 项目…...

【PmHub面试篇】PmHub 整合 TransmittableThreadLocal(TTL)缓存用户数据面试专题解析

你好&#xff0c;欢迎来到本次关于PmHub整合TransmittableThreadLocal (TTL)缓存用户数据的面试系列分享。在这篇文章中&#xff0c;我们将深入探讨这一技术领域的相关面试题预测。若想对相关内容有更透彻的理解&#xff0c;强烈推荐参考之前发布的博文&#xff1a;【PmHub后端…...

unity随机生成未知符号教程

目录 前言方法1方法2脚本后言示例代码 前言 在某些游戏中&#xff0c;有一些让人感到意味不明的未知符号&#xff0c;例如在游戏《巴别塔圣歌》中&#xff0c;就有这样一些能让人在初次就看不懂的未知符号。 或者在其他时候&#xff0c;这些未知符号如果跟粒子系统结合在一起的…...

基于RK3576+FPGA+AI工业控制器的工地防护检测装备解决方案

1.2.1 工地防护检测技术研究现状 在建筑施工的过程中&#xff0c;工人被要求暴露在危险的环境中作业 [2]。因此&#xff0c;防护装备 对于工人的安全与健康具有非常重要的意义[3]。工地工人必须佩戴适当的防护装备&#xff0c; 以降低意外伤害的风险。在过去的几十年里&#x…...

推荐一款PDF压缩的工具

今天一位小伙伴找来&#xff0c;问我有没有办法将PDF变小的办法。 详细了解了一下使用场景&#xff1a; 小伙伴要在某系统上传一个PDF文件&#xff0c;原文件是11.6MB&#xff0c;但是上传时系统做了限制&#xff0c;只能上传小于10MB的文件&#xff0c;如图&#xff1a; 我听…...

混沌映射(Chaotic Map)

一.定义 混沌映射是指一类具有混沌行为的离散时间非线性动力系统&#xff0c;通常由递推公式定义。其数学形式为 &#xff0c;其中 f 是非线性函数&#xff0c;θ 为参数。它们以简单的数学规则生成复杂的、看似随机的轨迹&#xff0c;是非线性动力学和混沌理论的重要研究对象…...

MySQL对数据库用户的操作

注&#xff1a;‘%’&#xff1a;表示允许远程连接&#xff0c;‘localhost’ &#xff1a;限制本地登陆 – 根据用户名、权限查询用户 SELECT USER FROM mysql.user WHERE USER‘your_name’ AND HOST‘%’; – 彻底删除用户 DROP USER ‘appuser’‘%’; – 刷新使其生效 FL…...

《PyTorch Hub:解锁深度学习模型的百宝箱》

走进 PyTorch Hub 在当今的深度学习领域,模型的复用和共享已成为推动技术飞速发展的关键力量。随着深度学习在计算机视觉、自然语言处理、语音识别等众多领域取得突破性进展,研究人员和开发者们不断探索更高效、更强大的模型架构。然而,从头开始训练一个深度学习模型往往需要…...

数据结构 堆与优先级队列

文章目录 &#x1f4d5;1. 堆(Heap)✏️1.1 堆的概念✏️1.2 堆的存储方式✏️1.3 堆的创建✏️1.4 堆的插入✏️1.5 堆的删除 &#x1f4d5;2. 优先级队列(PriorityQueue)✏️2.1 堆与优先级队列的关系✏️2.2 优先级队列的构造方法✏️2.3 优先级队列的常用方法 3. Java对象的…...

Leetcode 3569. Maximize Count of Distinct Primes After Split

Leetcode 3569. Maximize Count of Distinct Primes After Split 1. 解题思路2. 代码实现 题目链接&#xff1a;3569. Maximize Count of Distinct Primes After Split 1. 解题思路 这一题的话思路倒是还好&#xff0c;显然&#xff0c;要找出所有distinct的质数的切分&…...

用好 ImageFX,解锁游戏素材生成新姿势:从入门到进阶

用好 ImageFX&#xff0c;解锁游戏素材生成新姿势&#xff1a;从入门到进阶 (备注)大陆ip无法访问到imagefx 地址:https://labs.google/fx/zh/tools/image-fx 对于独立游戏开发者和小型团队而言&#xff0c;美术资源往往是项目推进中的一大痛点。预算有限、专业美术人员缺乏…...

unix/linux,sudo,其基本属性、语法、操作、api

现在我们要深入到sudo的“微观结构”了——它的属性、语法、操作以及是否有传统意义上的“API”。这就像我们从宏观的宇宙现象深入到基本粒子的相互作用一样,充满了探索的乐趣! 一、 sudo 的基本属性 (Fundamental Attributes) 这些属性是sudo作为一款软件和系统工具的核心…...

文本内容变化引起布局尺寸变化 导致的 UI 适配问题

在使用 Flutter 开发应用时&#xff0c;配合 easy_localization 实现多语言切换是一个非常常见的做法。但正如你所说&#xff0c;在不同语言下文字长度差异较大&#xff08;如英文和中文、阿拉伯语等&#xff09;会导致界面布局错位、UI 不美观的问题。 这个问题本质上是 文本…...