13.文本模块
13.1 知识点
文本模块的作用
在游戏开发中,我们经常需要对字符串进行各种处理操作,例如:
- 字符串拆分:拆分配置表中的字符串数据
- 数字格式化:将数字转换为指定格式的字符串
- 时间格式化:将秒数转换为”时分秒”格式
- 大数值显示:将过大的数值转换为”亿”、”万”等单位显示
常见场景:
- 配置表中存储格式如:
"1,10;2,4;32,1"(道具ID和数量) - 界面显示时间倒计时:
"1时23分45秒" - 显示格式化数字:
"0001"、"01:30:00" - 显示大数值:
"123万4千"、"56亿7千万"
传统方式的问题:
- 重复编写代码:每次都需要重新编写拆分、格式化逻辑
- 符号兼容性:中英文符号混用导致解析失败
- 格式不统一:不同开发者实现相同的逻辑可能有不同的格式
- 难以维护:格式化逻辑分散在各处,修改困难
解决方案:
通过统一的文本工具类,封装常用的字符串处理逻辑,提供一致的API接口。
主要功能:
- 字符串拆分:支持多种分隔符(
;,,,%,:, 空格,|,_) - 数字格式化:数字前补0,保留小数位
- 时间转换:秒转时分秒格式,支持自定义分隔符
- 大数值显示:自动转换为”亿”、”万”等单位
文本模块的基本原理
设计理念:
- 工具类设计:使用静态类提供工具方法
- 灵活配置:支持多种分隔符和格式化选项
- 容错处理:自动处理中英文符号混用问题
- 性能优化:使用StringBuilder减少字符串拼接开销
核心功能:
- 字符串拆分:基于
string.Split()方法封装 - 类型转换:自动将字符串数组转换为数值数组
- 二次拆分:支持嵌套的键值对数据格式
- 数字格式化:使用
ToString("Dn")和ToString("Fn")格式化 - 时间计算:通过除法和取模计算时分秒
- 大数值转换:按量级转换为不同单位
文本模块实现
为什么需要文本模块
传统方式的问题:
// 传统方式1:直接使用string.Split
public class ItemManager
{
void ParseItemData(string data)
{
string[] items = data.Split(';');
foreach (string item in items)
{
string[] parts = item.Split(',');
int id = int.Parse(parts[0]);
int count = int.Parse(parts[1]);
// 处理道具...
}
}
}
存在的问题:
- 符号混用:如果配置表中使用了中文符号(
;而不是;),会解析失败 - 代码重复:每个需要解析的地方都要重复编写拆分逻辑
- 容错性差:没有空字符串检查和类型转换容错
- 不可维护:格式变化需要修改多处代码
传统方式2:时间格式化代码重复
// 传统方式:每次都要计算时分秒
public class GameUI : MonoBehaviour
{
void UpdateTimer(int seconds)
{
int hour = seconds / 3600;
int minute = (seconds % 3600) / 60;
int second = seconds % 60;
string timeStr = $"{hour:D2}:{minute:D2}:{second:D2}";
timeText.text = timeStr;
}
}
问题:
- 逻辑重复:每次需要显示时间都要重复计算
- 格式不统一:不同地方可能有不同的显示格式
- 维护困难:如果需要修改时间显示格式,需要修改多处代码
字符串拆分功能
应用场景:
- 配置表数据解析:”1,10;2,20;3,30” 解析为道具ID和数量
- 关卡配置解析:”10|20|30” 解析为关卡ID列表
- 路径数据解析:”0,0:100,100:200,200” 解析为坐标点
核心功能:
- 自动符号兼容:中英文符号自动转换(
;→;,,→,) - 多种分隔符支持:分号、逗号、百分号、冒号、空格、竖线、下划线
- 类型自动转换:字符串数组自动转换为整数数组
- 二次拆分支持:嵌套的键值对数据格式
实现代码:
/// <summary>
/// 拆分字符串,返回字符串数组
/// 自动处理中英文符号混用问题
/// </summary>
public static string[] SplitStr(string str, int type = 1)
{
if (str == "")
return new string[0];
string newStr = str;
if (type == 1)
{
// 中文分号自动转换为英文分号
while (newStr.IndexOf(";") != -1)
newStr = newStr.Replace(";", ";");
return newStr.Split(';');
}
else if (type == 2)
{
// 中文逗号自动转换为英文逗号
while (newStr.IndexOf(",") != -1)
newStr = newStr.Replace(",", ",");
return newStr.Split(',');
}
// ... 其他分隔符类似处理
return new string[0];
}
/// <summary>
/// 拆分字符串,返回整数数组
/// 自动进行类型转换
/// </summary>
public static int[] SplitStrToIntArr(string str, int type = 1)
{
string[] strs = SplitStr(str, type);
if (strs.Length == 0)
return new int[0];
// 将字符串数组转换为整数数组
return Array.ConvertAll<string, int>(strs, (s) =>
{
return int.Parse(s);
});
}
/// <summary>
/// 拆分多组键值对数据
/// 例如:"1,10;2,20" → 回调(id:1,count:10), (id:2,count:20)
/// </summary>
public static void SplitStrToIntArrTwice(string str, int typeOne, int typeTwo,
UnityAction<int, int> callBack)
{
string[] strs = SplitStr(str, typeOne); // 第一次拆分:按分号拆
for (int i = 0; i < strs.Length; i++)
{
int[] ints = SplitStrToIntArr(strs[i], typeTwo); // 第二次拆分:按逗号拆
if (ints.Length >= 2)
callBack.Invoke(ints[0], ints[1]); // 调用回调传递键值对
}
}
使用示例:
// 场景1:解析配置表数据
string data = "1,10;2,20;3,30";
TextUtil.SplitStrToIntArrTwice(data, 1, 2, (itemID, count) =>
{
Debug.Log($"道具ID: {itemID}, 数量: {count}");
// 输出:
// 道具ID: 1, 数量: 10
// 道具ID: 2, 数量: 20
// 道具ID: 3, 数量: 30
});
数字前补0转字符串功能
应用场景:
- 玩家等级显示:1 → “001”
- 章节编号显示:5 → “005”
- 排行榜序号:23 → “023”
核心功能:
- 指定长度格式化:使用
ToString("Dn")格式化数字 - 自动补0:长度不够自动在前面补0
- 保留原值:超出长度保留原始数值
实现代码:
/// <summary>
/// 得到指定长度的数字转字符串内容
/// 使用D格式化选项:D3表示3位,不足补0
/// </summary>
public static string GetNumStr(int value, int len)
{
// ToString("Dn") 将数字转换为长度为n的字符串
// 不足长度时在前面补0
return value.ToString($"D{len}");
}
/// <summary>
/// 让指定浮点数保留小数点后n位
/// 使用F格式化选项:F2表示保留2位小数
/// </summary>
public static string GetDecimalStr(float value, int len)
{
// ToString("Fn") 保留小数点后n位
return value.ToString($"F{len}");
}
使用示例:
// 格式化玩家等级
int level = 15;
string levelStr = TextUtil.GetNumStr(level, 3);
Debug.Log($"玩家等级:{levelStr}"); // 输出:玩家等级:015
// 保留小数位
float score = 95.6789f;
string scoreStr = TextUtil.GetDecimalStr(score, 2);
Debug.Log($"得分:{scoreStr}"); // 输出:得分:95.68
秒转时分秒功能
应用场景:
- 游戏倒计时显示:”2时30分45秒”
- 技能冷却时间:”1分30秒”
- 任务剩余时间:”0时5分30秒”
核心功能:
- 时分秒计算:通过除法(
/)和取模(%)计算时分秒 - 灵活显示:支持是否忽略0值前缀
- 自定义格式:可以自定义时分秒的拼接字符
- 固定长度:可选保留至少2位数字(01:05:30)
实现代码:
/// <summary>
/// 秒转时分秒格式
/// 计算逻辑:
/// - 小时 = 总秒数 / 3600
/// - 分钟 = (总秒数 % 3600) / 60
/// - 秒 = 总秒数 % 60
/// </summary>
public static string SecondToHMS(int s, bool egZero = false, bool isKeepLen = false,
string hourStr = "时", string minuteStr = "分", string secondStr = "秒")
{
if (s < 0) s = 0; // 负数归0
// 计算时分秒
int hour = s / 3600;
int minute = (s % 3600) / 60;
int second = s % 60;
resultStr.Clear();
// 根据egZero参数决定是否显示0值
if (hour != 0 || !egZero)
{
resultStr.Append(isKeepLen ? GetNumStr(hour, 2) : hour);
resultStr.Append(hourStr);
}
// 如果小时不为0,则分钟和秒必须显示
if (minute != 0 || !egZero || hour != 0)
{
resultStr.Append(isKeepLen ? GetNumStr(minute, 2) : minute);
resultStr.Append(minuteStr);
}
if (second != 0 || !egZero || hour != 0 || minute != 0)
{
resultStr.Append(isKeepLen ? GetNumStr(second, 2) : second);
resultStr.Append(secondStr);
}
// 0秒时显示0
if (resultStr.Length == 0)
{
resultStr.Append(0);
resultStr.Append(secondStr);
}
return resultStr.ToString();
}
使用示例:
// 标准格式
string time1 = TextUtil.SecondToHMS(3661);
Debug.Log(time1); // 输出:1时1分1秒
// 忽略0值
string time2 = TextUtil.SecondToHMS(65, true);
Debug.Log(time2); // 输出:1分5秒(小时为0被忽略)
// 自定义格式
string time3 = TextUtil.SecondToHMS(3661, false, false, "h ", "m ", "s");
Debug.Log(time3); // 输出:1h 1m 1s
秒转00:00:00格式功能
应用场景:
- 游戏计时器:”01:23:45”
- 视频时长:”00:05:30”
- 战斗倒计时:”00:02:30”
核心功能:
- 固定格式:使用冒号作为分隔符
- 固定长度:时分秒都是2位数字
- 简洁调用:提供便捷方法直接返回固定格式
实现代码:
/// <summary>
/// 秒转00:00:00格式
/// 调用SecondToHMS方法,设置isKeepLen=true以保留2位数字
/// </summary>
public static string SecondToHMS2(int s, bool egZero = false)
{
// 参数说明:
// egZero: 是否忽略0值
// true: isKeepLen,保留至少2位
// ":", ":", "": 使用冒号分隔,秒后面不加字符
return SecondToHMS(s, egZero, true, ":", ":", "");
}
使用示例:
// 标准格式
string time1 = TextUtil.SecondToHMS2(3661);
Debug.Log(time1); // 输出:01:01:01
// 忽略0值(仍然保留至少2位)
string time2 = TextUtil.SecondToHMS2(65, true);
Debug.Log(time2); // 输出:00:01:05
// 游戏倒计时显示
string countdown = TextUtil.SecondToHMS2(5025);
// countdownText.text = countdown; // 显示:01:23:45
大数据数值转字符串功能
应用场景:
- 玩家金币显示:999999 → “99万9千”
- 排行榜分数:1245000000 → “12亿4千万”
- 战斗伤害:5689523 → “568万9千”
核心功能:
- 自动分级:根据数值大小选择”亿”或”万”单位
- 二级显示:除了主要单位,还显示次要单位(千万、千)
- 简洁显示:大数值转换为易读的格式
实现代码:
/// <summary>
/// 将大数据转换为易读的字符串
/// 分级处理:
/// - >= 1亿:显示"n亿n千万"
/// - >= 1万:显示"n万n千"
/// - < 1万:直接显示数值
/// </summary>
public static string GetBigDataToString(int num)
{
if (num >= 100000000) // 大于等于1亿
{
return BigDataChange(num, 100000000, "亿", "千万");
}
else if (num >= 10000) // 大于等于1万
{
return BigDataChange(num, 10000, "万", "千");
}
else // 小于1万
{
return num.ToString();
}
}
/// <summary>
/// 大数据转换核心逻辑
/// 计算主要单位:num / company
/// 计算次要单位:(num % company) / (company / 10)
/// 例如:123456789
/// - 主要:123456789 / 100000000 = 1(亿)
/// - 次要:(123456789 % 100000000) / 10000000 = 2(千万)
/// </summary>
private static string BigDataChange(int num, int company, string bigCompany, string littleCompany)
{
resultStr.Clear();
// 计算主要单位(几亿、几万)
int mainNum = num / company;
resultStr.Append(mainNum);
resultStr.Append(bigCompany);
// 计算次要单位(几千万、几千)
int tmpNum = num % company; // 取余数
tmpNum /= (company / 10); // 除以十分之一得到次要单位数字
// 如果次要单位不为0,才显示
if (tmpNum != 0)
{
resultStr.Append(tmpNum);
resultStr.Append(littleCompany);
}
return resultStr.ToString();
}
使用示例:
// 亿级别
string big1 = TextUtil.GetBigDataToString(1245000000);
Debug.Log(big1); // 输出:12亿4千万
// 万级别
string big2 = TextUtil.GetBigDataToString(5689523);
Debug.Log(big2); // 输出:568万9千
// 普通数值
string big3 = TextUtil.GetBigDataToString(9999);
Debug.Log(big3); // 输出:9999
// 用户金币显示
int playerGold = 999999;
string goldStr = TextUtil.GetBigDataToString(playerGold);
// goldText.text = goldStr; // 显示:99万9千
测试和使用
完整的TextUtil类结构:
using System;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 文本工具类
/// 提供字符串拆分、数字格式化、时间转换、大数值显示等功能
/// </summary>
public class TextUtil
{
// 使用StringBuilder优化字符串拼接性能
private static StringBuilder resultStr = new StringBuilder("");
// 字符串拆分相关方法
public static string[] SplitStr(string str, int type = 1);
public static int[] SplitStrToIntArr(string str, int type = 1);
public static void SplitStrToIntArrTwice(string str, int typeOne, int typeTwo,
UnityAction<int, int> callBack);
public static void SplitStrTwice(string str, int typeOne, int typeTwo,
UnityAction<string, string> callBack);
// 数字格式化相关方法
public static string GetNumStr(int value, int len);
public static string GetDecimalStr(float value, int len);
// 时间转换相关方法
public static string SecondToHMS(int s, bool egZero = false, bool isKeepLen = false,
string hourStr = "时", string minuteStr = "分", string secondStr = "秒");
public static string SecondToHMS2(int s, bool egZero = false);
// 大数值转换相关方法
public static string GetBigDataToString(int num);
private static string BigDataChange(int num, int company, string bigCompany, string littleCompany);
}
```csharp
using System;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 文本工具类
/// 用于处理字符串的一些公共功能
/// 提供字符串拆分、数字格式化、时间转换等功能
/// </summary>
public class TextUtil
{
#region 字段
/// <summary>
/// 字符串拼接工具,用于高效拼接字符串
/// </summary>
private static StringBuilder resultStr = new StringBuilder("");
#endregion
#region 字符串拆分
/// <summary>
/// 拆分字符串,返回字符串数组
/// 自动处理中英文符号混用问题
/// </summary>
/// <param name="str">想要被拆分的字符串</param>
/// <param name="type">拆分字符类型:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <returns>拆分后的字符串数组</returns>
public static string[] SplitStr(string str, int type = 1)
{
if (str == "")
return new string[0];
string newStr = str;
if (type == 1)
{
// 为了避免英文符号填成了中文符号 我们先进行一个替换
while (newStr.IndexOf(";") != -1)
newStr = newStr.Replace(";", ";");
return newStr.Split(';');
}
else if (type == 2)
{
// 为了避免英文符号填成了中文符号 我们先进行一个替换
while (newStr.IndexOf(",") != -1)
newStr = newStr.Replace(",", ",");
return newStr.Split(',');
}
else if (type == 3)
{
return newStr.Split('%');
}
else if (type == 4)
{
// 为了避免英文符号填成了中文符号 我们先进行一个替换
while (newStr.IndexOf(":") != -1)
newStr = newStr.Replace(":", ":");
return newStr.Split(':');
}
else if (type == 5)
{
return newStr.Split(' ');
}
else if (type == 6)
{
return newStr.Split('|');
}
else if (type == 7)
{
return newStr.Split('_');
}
return new string[0];
}
/// <summary>
/// 拆分字符串,返回整数数组
/// </summary>
/// <param name="str">想要被拆分的字符串</param>
/// <param name="type">拆分字符类型:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <returns>拆分后的整数数组</returns>
public static int[] SplitStrToIntArr(string str, int type = 1)
{
// 得到拆分后的字符串数组
string[] strs = SplitStr(str, type);
if (strs.Length == 0)
return new int[0];
// 把字符串数组转换成int数组
return Array.ConvertAll<string, int>(strs, (s) =>
{
return int.Parse(s);
});
}
/// <summary>
/// 专门用来拆分多组键值对形式的数据的,以int返回
/// 例如:"1,10;2,4;32,1" 会被拆分为多组键值对
/// </summary>
/// <param name="str">待拆分的字符串</param>
/// <param name="typeOne">组间分隔符:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <param name="typeTwo">键值对分隔符:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <param name="callBack">回调函数,参数为键和值</param>
public static void SplitStrToIntArrTwice(string str, int typeOne, int typeTwo, UnityAction<int, int> callBack)
{
string[] strs = SplitStr(str, typeOne);
if (strs.Length == 0)
return;
int[] ints;
for (int i = 0; i < strs.Length; i++)
{
// 拆分单个道具的ID和数量信息
ints = SplitStrToIntArr(strs[i], typeTwo);
if (ints.Length == 0)
continue;
callBack.Invoke(ints[0], ints[1]);
}
}
/// <summary>
/// 专门用来拆分多组键值对形式的数据的,以string返回
/// </summary>
/// <param name="str">待拆分的字符串</param>
/// <param name="typeOne">组间分隔符:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <param name="typeTwo">键值对分隔符:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <param name="callBack">回调函数,参数为键和值</param>
public static void SplitStrTwice(string str, int typeOne, int typeTwo, UnityAction<string, string> callBack)
{
string[] strs = SplitStr(str, typeOne);
if (strs.Length == 0)
return;
string[] strs2;
for (int i = 0; i < strs.Length; i++)
{
// 拆分单个道具的ID和数量信息
strs2 = SplitStr(strs[i], typeTwo);
if (strs2.Length == 0)
continue;
callBack.Invoke(strs2[0], strs2[1]);
}
}
#endregion
#region 数字转字符串
/// <summary>
/// 得到指定长度的数字转字符串内容
/// 如果长度不够会在前面补0,如果长度超出会保留原始数值
/// </summary>
/// <param name="value">数值</param>
/// <param name="len">长度</param>
/// <returns>格式化后的字符串</returns>
public static string GetNumStr(int value, int len)
{
// ToString中传入一个 Dn 的字符串
// 代表想要将数字转换为长度为n的字符串
// 如果长度不够会在前面补0
return value.ToString($"D{len}");
}
/// <summary>
/// 让指定浮点数保留小数点后n位
/// </summary>
/// <param name="value">具体的浮点数</param>
/// <param name="len">保留小数点后n位</param>
/// <returns>格式化后的字符串</returns>
public static string GetDecimalStr(float value, int len)
{
// ToString中传入一个 Fn 的字符串
// 代表想要保留小数点后几位小数
return value.ToString($"F{len}");
}
#endregion
#region 时间转换
/// <summary>
/// 秒转时分秒格式,其中时分秒可以自己传
/// </summary>
/// <param name="s">秒数</param>
/// <param name="egZero">是否忽略0</param>
/// <param name="isKeepLen">是否保留至少2位</param>
/// <param name="hourStr">小时的拼接字符</param>
/// <param name="minuteStr">分钟的拼接字符</param>
/// <param name="secondStr">秒的拼接字符</param>
/// <returns>格式化后的时间字符串</returns>
public static string SecondToHMS(int s, bool egZero = false, bool isKeepLen = false, string hourStr = "时", string minuteStr = "分", string secondStr = "秒")
{
// 时间不会有负数,所以我们如果发现是负数直接归0
if (s < 0)
s = 0;
// 计算小时
int hour = s / 3600;
// 除去小时后的剩余秒
int second = s % 3600;
// 剩余秒转为分钟数
int minute = second / 60;
// 计算秒
second = s % 60;
// 拼接
resultStr.Clear();
// 如果小时不为0 或者 不忽略0
if (hour != 0 || !egZero)
{
resultStr.Append(isKeepLen ? GetNumStr(hour, 2) : hour);
resultStr.Append(hourStr);
}
// 如果分钟不为0 或者 不忽略0 或者 小时不为0
if (minute != 0 || !egZero || hour != 0)
{
resultStr.Append(isKeepLen ? GetNumStr(minute, 2) : minute);
resultStr.Append(minuteStr);
}
// 如果秒不为0 或者 不忽略0 或者 小时和分钟不为0
if (second != 0 || !egZero || hour != 0 || minute != 0)
{
resultStr.Append(isKeepLen ? GetNumStr(second, 2) : second);
resultStr.Append(secondStr);
}
// 如果传入的参数是0秒时
if (resultStr.Length == 0)
{
resultStr.Append(0);
resultStr.Append(secondStr);
}
return resultStr.ToString();
}
/// <summary>
/// 秒转00:00:00格式
/// </summary>
/// <param name="s">秒数</param>
/// <param name="egZero">是否忽略0</param>
/// <returns>格式化后的时间字符串</returns>
public static string SecondToHMS2(int s, bool egZero = false)
{
return SecondToHMS(s, egZero, true, ":", ":", "");
}
#endregion
#region 大数值转换
/// <summary>
/// 将较大较长的数转换为字符串
/// 例如:1198678978 转换为 11亿9千万
/// 例如:5328987 转换为 532万8千
/// </summary>
/// <param name="num">具体数值</param>
/// <returns>格式化后的大数值字符串</returns>
public static string GetBigDataToString(int num)
{
// 如果大于1亿,那么就显示 n亿n千万
if (num >= 100000000)
{
return BigDataChange(num, 100000000, "亿", "千万");
}
// 如果大于1万,那么就显示 n万n千
else if (num >= 10000)
{
return BigDataChange(num, 10000, "万", "千");
}
// 都不满足,就直接显示数值本身
else
return num.ToString();
}
/// <summary>
/// 把大数据转换成对应的字符串拼接
/// </summary>
/// <param name="num">数值</param>
/// <param name="company">分割单位,可以填 100000000、10000</param>
/// <param name="bigCompany">大单位(亿、万)</param>
/// <param name="littleCompany">小单位(万、千)</param>
/// <returns>格式化后的字符串</returns>
private static string BigDataChange(int num, int company, string bigCompany, string littleCompany)
{
resultStr.Clear();
// 有几亿、几万
resultStr.Append(num / company);
resultStr.Append(bigCompany);
// 有几千万、几千
int tmpNum = num % company;
// 看有几千万、几千
tmpNum /= (company / 10);
// 算出来不为0
if (tmpNum != 0)
{
resultStr.Append(tmpNum);
resultStr.Append(littleCompany);
}
return resultStr.ToString();
}
#endregion
}
字符串拆分使用示例:
using UnityEngine;
public class TextUtilSplitExample : MonoBehaviour
{
void Start()
{
Debug.Log("=== 字符串拆分示例 ===");
// 示例1:基础拆分
string data1 = "1;2;3;4;5";
string[] strs1 = TextUtil.SplitStr(data1, 1); // 使用分号分隔
Debug.Log($"拆分结果:{string.Join(", ", strs1)}");
// 输出:拆分结果:1, 2, 3, 4, 5
// 示例2:中文符号自动转换
string data2 = "10,20,30"; // 中文逗号
int[] ints2 = TextUtil.SplitStrToIntArr(data2, 2); // 使用逗号分隔
Debug.Log($"转换为int数组:{string.Join(", ", ints2)}");
// 输出:转换为int数组:10, 20, 30
// 示例3:多组键值对拆分
string data3 = "1,10;2,20;3,30"; // 格式:道具ID,数量;道具ID,数量
TextUtil.SplitStrToIntArrTwice(data3, 1, 2, (id, count) =>
{
Debug.Log($"道具ID: {id}, 数量: {count}");
});
// 输出:
// 道具ID: 1, 数量: 10
// 道具ID: 2, 数量: 20
// 道具ID: 3, 数量: 30
}
}
数字格式化使用示例:
using UnityEngine;
public class TextUtilFormatExample : MonoBehaviour
{
void Start()
{
Debug.Log("=== 数字格式化示例 ===");
// 示例1:数字前补0
string num1 = TextUtil.GetNumStr(1, 4);
Debug.Log($"1格式化为4位:{num1}");
// 输出:1格式化为4位:0001
string num2 = TextUtil.GetNumStr(100, 3);
Debug.Log($"100格式化为3位:{num2}");
// 输出:100格式化为3位:100(超出长度保留原值)
// 示例2:保留小数位
string decimal1 = TextUtil.GetDecimalStr(3.1415926f, 2);
Debug.Log($"3.1415926保留2位小数:{decimal1}");
// 输出:3.1415926保留2位小数:3.14
string decimal2 = TextUtil.GetDecimalStr(10.5f, 2);
Debug.Log($"10.5保留2位小数:{decimal2}");
// 输出:10.5保留2位小数:10.50
}
}
时间转换使用示例:
using UnityEngine;
public class TextUtilTimeExample : MonoBehaviour
{
void Start()
{
Debug.Log("=== 时间转换示例 ===");
// 示例1:转换为时分秒格式
string time1 = TextUtil.SecondToHMS(3661);
Debug.Log($"3661秒 = {time1}");
// 输出:3661秒 = 1时1分1秒
// 示例2:忽略0值
string time2 = TextUtil.SecondToHMS(3661, true);
Debug.Log($"3661秒(忽略0)= {time2}");
// 输出:3661秒(忽略0)= 1时1分1秒
string time3 = TextUtil.SecondToHMS(65, true);
Debug.Log($"65秒(忽略0)= {time3}");
// 输出:65秒(忽略0)= 1分5秒(小时为0被忽略)
// 示例3:转换为00:00:00格式
string time4 = TextUtil.SecondToHMS2(3661);
Debug.Log($"3661秒 = {time4}");
// 输出:3661秒 = 01:01:01
string time5 = TextUtil.SecondToHMS2(65);
Debug.Log($"65秒 = {time5}");
// 输出:65秒 = 00:01:05
}
}
大数值转换使用示例:
using UnityEngine;
public class TextUtilBigNumExample : MonoBehaviour
{
void Start()
{
Debug.Log("=== 大数值转换示例 ===");
// 示例1:亿级别
string big1 = TextUtil.GetBigDataToString(1245000000);
Debug.Log($"1245000000 = {big1}");
// 输出:1245000000 = 12亿4千万
// 示例2:万级别
string big2 = TextUtil.GetBigDataToString(5689523);
Debug.Log($"5689523 = {big2}");
// 输出:5689523 = 568万9千
// 示例3:普通数值
string big3 = TextUtil.GetBigDataToString(5869);
Debug.Log($"5869 = {big3}");
// 输出:5869 = 5869
string big4 = TextUtil.GetBigDataToString(10000);
Debug.Log($"10000 = {big4}");
// 输出:10000 = 1万
}
}
完整的实际应用示例:
using UnityEngine;
/// <summary>
/// 文本模块实际应用示例
/// </summary>
public class TextUtilCompleteExample : MonoBehaviour
{
#region 场景1:配置表数据解析
/// <summary>
/// 解析背包物品配置
/// </summary>
void Example_ParseBackpackItems()
{
Debug.Log("=== 解析背包物品配置 ===");
// 配置格式:"道具ID,数量;道具ID,数量"
string backpackData = "1,10;2,20;3,5";
TextUtil.SplitStrToIntArrTwice(backpackData, 1, 2, (itemID, count) =>
{
Debug.Log($"道具ID: {itemID}, 数量: {count}");
// 这里可以添加到背包
// BackpackManager.AddItem(itemID, count);
});
// 输出:
// 道具ID: 1, 数量: 10
// 道具ID: 2, 数量: 20
// 道具ID: 3, 数量: 5
}
#endregion
#region 场景2:界面倒计时显示
/// <summary>
/// 更新倒计时UI显示
/// </summary>
void Example_UpdateCountdownUI(int seconds)
{
Debug.Log("=== 更新倒计时UI ===");
// 转换为00:00:00格式
string timeStr = TextUtil.SecondToHMS2(seconds);
Debug.Log($"倒计时:{timeStr}");
// 输出:倒计时:01:23:45
// UI更新
// countdownText.text = timeStr;
}
#endregion
#region 场景3:玩家属性显示
/// <summary>
/// 显示玩家排行榜信息
/// </summary>
void Example_DisplayPlayerRankInfo()
{
Debug.Log("=== 显示玩家排行榜 ===");
// 玩家数据
int playerLevel = 15;
long playerScore = 12345678;
int playerCoin = 999999;
// 格式化显示
string levelStr = TextUtil.GetNumStr(playerLevel, 3);
string scoreStr = TextUtil.GetBigDataToString((int)playerScore);
string coinStr = TextUtil.GetBigDataToString(playerCoin);
Debug.Log($"玩家等级:{levelStr}"); // 015
Debug.Log($"玩家分数:{scoreStr}"); // 1234万5千
Debug.Log($"玩家金币:{coinStr}"); // 99万9千
}
#endregion
#region 场景4:游戏内时间显示
/// <summary>
/// 显示游戏内时间
/// </summary>
void Example_DisplayGameTime(int totalSeconds)
{
Debug.Log("=== 显示游戏内时间 ===");
// 标准格式
string time1 = TextUtil.SecondToHMS(totalSeconds);
Debug.Log($"游戏时间:{time1}");
// 输出:游戏时间:2时30分45秒
// 简略格式(忽略为0的前缀)
string time2 = TextUtil.SecondToHMS(45, true);
Debug.Log($"简略时间:{time2}");
// 输出:简略时间:45秒
// 英文格式
string time3 = TextUtil.SecondToHMS(3661, false, false, "h ", "m ", "s");
Debug.Log($"英文时间:{time3}");
// 输出:英文时间:1h 1m 1s
}
#endregion
void Start()
{
// 运行所有示例
Example_ParseBackpackItems();
Example_UpdateCountdownUI(5025);
Example_DisplayPlayerRankInfo();
Example_DisplayGameTime(9045);
}
}
进阶和拓展
进阶优化建议:
- 更多格式化选项:支持负数格式化、千分位分隔符等
- 多语言支持:时间的单位可以支持多语言(时/小时/hours)
- 自定义分隔符:支持传入任意自定义分隔符
- 批量处理:支持批量格式化多个数值
- 正则表达式:对于复杂格式使用正则表达式处理
- 性能优化:对于大量数据处理,使用对象池管理StringBuilder
- 扩展方法:提供扩展方法,让代码更简洁
注意事项:
- 空字符串处理:确保空字符串不会导致解析错误
- 类型转换容错:对于无法转换的数据要有容错机制
- 符号兼容性:自动处理中英文符号混用
- 性能考虑:大量数据处理时注意使用StringBuilder
- 格式一致性:所有格式化应遵循相同的规则
13.2 知识点代码
TextUtil.cs(文本工具类 - 最终版本)
using System;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 文本工具类
/// 用于处理字符串的一些公共功能
/// 提供字符串拆分、数字格式化、时间转换、大数值显示等功能
/// </summary>
public class TextUtil
{
#region 字段
/// <summary>
/// 字符串拼接工具,用于高效拼接字符串
/// </summary>
private static StringBuilder resultStr = new StringBuilder("");
#endregion
#region 字符串拆分
/// <summary>
/// 拆分字符串,返回字符串数组
/// 自动处理中英文符号混用问题
/// </summary>
/// <param name="str">想要被拆分的字符串</param>
/// <param name="type">拆分字符类型:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <returns>拆分后的字符串数组</returns>
public static string[] SplitStr(string str, int type = 1)
{
if (str == "")
return new string[0];
string newStr = str;
if (type == 1)
{
// 为了避免英文符号填成了中文符号 我们先进行一个替换
while (newStr.IndexOf(";") != -1)
newStr = newStr.Replace(";", ";");
return newStr.Split(';');
}
else if (type == 2)
{
// 为了避免英文符号填成了中文符号 我们先进行一个替换
while (newStr.IndexOf(",") != -1)
newStr = newStr.Replace(",", ",");
return newStr.Split(',');
}
else if (type == 3)
{
return newStr.Split('%');
}
else if (type == 4)
{
// 为了避免英文符号填成了中文符号 我们先进行一个替换
while (newStr.IndexOf(":") != -1)
newStr = newStr.Replace(":", ":");
return newStr.Split(':');
}
else if (type == 5)
{
return newStr.Split(' ');
}
else if (type == 6)
{
return newStr.Split('|');
}
else if (type == 7)
{
return newStr.Split('_');
}
return new string[0];
}
/// <summary>
/// 拆分字符串,返回整数数组
/// </summary>
/// <param name="str">想要被拆分的字符串</param>
/// <param name="type">拆分字符类型:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <returns>拆分后的整数数组</returns>
public static int[] SplitStrToIntArr(string str, int type = 1)
{
// 得到拆分后的字符串数组
string[] strs = SplitStr(str, type);
if (strs.Length == 0)
return new int[0];
// 把字符串数组转换成int数组
return Array.ConvertAll<string, int>(strs, (s) =>
{
return int.Parse(s);
});
}
/// <summary>
/// 专门用来拆分多组键值对形式的数据的,以int返回
/// 例如:"1,10;2,4;32,1" 会被拆分为多组键值对
/// </summary>
/// <param name="str">待拆分的字符串</param>
/// <param name="typeOne">组间分隔符:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <param name="typeTwo">键值对分隔符:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <param name="callBack">回调函数,参数为键和值</param>
public static void SplitStrToIntArrTwice(string str, int typeOne, int typeTwo, UnityAction<int, int> callBack)
{
string[] strs = SplitStr(str, typeOne);
if (strs.Length == 0)
return;
int[] ints;
for (int i = 0; i < strs.Length; i++)
{
// 拆分单个道具的ID和数量信息
ints = SplitStrToIntArr(strs[i], typeTwo);
if (ints.Length == 0)
continue;
callBack.Invoke(ints[0], ints[1]);
}
}
/// <summary>
/// 专门用来拆分多组键值对形式的数据的,以string返回
/// </summary>
/// <param name="str">待拆分的字符串</param>
/// <param name="typeOne">组间分隔符:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <param name="typeTwo">键值对分隔符:1-; 2-, 3-% 4-: 5-空格 6-| 7-_</param>
/// <param name="callBack">回调函数,参数为键和值</param>
public static void SplitStrTwice(string str, int typeOne, int typeTwo, UnityAction<string, string> callBack)
{
string[] strs = SplitStr(str, typeOne);
if (strs.Length == 0)
return;
string[] strs2;
for (int i = 0; i < strs.Length; i++)
{
// 拆分单个道具的ID和数量信息
strs2 = SplitStr(strs[i], typeTwo);
if (strs2.Length == 0)
continue;
callBack.Invoke(strs2[0], strs2[1]);
}
}
#endregion
#region 数字转字符串
/// <summary>
/// 得到指定长度的数字转字符串内容
/// 如果长度不够会在前面补0,如果长度超出会保留原始数值
/// </summary>
/// <param name="value">数值</param>
/// <param name="len">长度</param>
/// <returns>格式化后的字符串</returns>
public static string GetNumStr(int value, int len)
{
// ToString中传入一个 Dn 的字符串
// 代表想要将数字转换为长度为n的字符串
// 如果长度不够会在前面补0
return value.ToString($"D{len}");
}
/// <summary>
/// 让指定浮点数保留小数点后n位
/// </summary>
/// <param name="value">具体的浮点数</param>
/// <param name="len">保留小数点后n位</param>
/// <returns>格式化后的字符串</returns>
public static string GetDecimalStr(float value, int len)
{
// ToString中传入一个 Fn 的字符串
// 代表想要保留小数点后几位小数
return value.ToString($"F{len}");
}
#endregion
#region 时间转换
/// <summary>
/// 秒转时分秒格式,其中时分秒可以自己传
/// </summary>
/// <param name="s">秒数</param>
/// <param name="egZero">是否忽略0</param>
/// <param name="isKeepLen">是否保留至少2位</param>
/// <param name="hourStr">小时的拼接字符</param>
/// <param name="minuteStr">分钟的拼接字符</param>
/// <param name="secondStr">秒的拼接字符</param>
/// <returns>格式化后的时间字符串</returns>
public static string SecondToHMS(int s, bool egZero = false, bool isKeepLen = false, string hourStr = "时", string minuteStr = "分", string secondStr = "秒")
{
// 时间不会有负数,所以我们如果发现是负数直接归0
if (s < 0)
s = 0;
// 计算小时
int hour = s / 3600;
// 除去小时后的剩余秒
int second = s % 3600;
// 剩余秒转为分钟数
int minute = second / 60;
// 计算秒
second = s % 60;
// 拼接
resultStr.Clear();
// 如果小时不为0 或者 不忽略0
if (hour != 0 || !egZero)
{
resultStr.Append(isKeepLen ? GetNumStr(hour, 2) : hour);
resultStr.Append(hourStr);
}
// 如果分钟不为0 或者 不忽略0 或者 小时不为0
if (minute != 0 || !egZero || hour != 0)
{
resultStr.Append(isKeepLen ? GetNumStr(minute, 2) : minute);
resultStr.Append(minuteStr);
}
// 如果秒不为0 或者 不忽略0 或者 小时和分钟不为0
if (second != 0 || !egZero || hour != 0 || minute != 0)
{
resultStr.Append(isKeepLen ? GetNumStr(second, 2) : second);
resultStr.Append(secondStr);
}
// 如果传入的参数是0秒时
if (resultStr.Length == 0)
{
resultStr.Append(0);
resultStr.Append(secondStr);
}
return resultStr.ToString();
}
/// <summary>
/// 秒转00:00:00格式
/// </summary>
/// <param name="s">秒数</param>
/// <param name="egZero">是否忽略0</param>
/// <returns>格式化后的时间字符串</returns>
public static string SecondToHMS2(int s, bool egZero = false)
{
return SecondToHMS(s, egZero, true, ":", ":", "");
}
#endregion
#region 大数值转换
/// <summary>
/// 将较大较长的数转换为字符串
/// 例如:1198678978 转换为 11亿9千万
/// 例如:5328987 转换为 532万8千
/// </summary>
/// <param name="num">具体数值</param>
/// <returns>格式化后的大数值字符串</returns>
public static string GetBigDataToString(int num)
{
// 如果大于1亿,那么就显示 n亿n千万
if (num >= 100000000)
{
return BigDataChange(num, 100000000, "亿", "千万");
}
// 如果大于1万,那么就显示 n万n千
else if (num >= 10000)
{
return BigDataChange(num, 10000, "万", "千");
}
// 都不满足,就直接显示数值本身
else
return num.ToString();
}
/// <summary>
/// 把大数据转换成对应的字符串拼接
/// </summary>
/// <param name="num">数值</param>
/// <param name="company">分割单位,可以填 100000000、10000</param>
/// <param name="bigCompany">大单位(亿、万)</param>
/// <param name="littleCompany">小单位(万、千)</param>
/// <returns>格式化后的字符串</returns>
private static string BigDataChange(int num, int company, string bigCompany, string littleCompany)
{
resultStr.Clear();
// 有几亿、几万
resultStr.Append(num / company);
resultStr.Append(bigCompany);
// 有几千万、几千
int tmpNum = num % company;
// 看有几千万、几千
tmpNum /= (company / 10);
// 算出来不为0
if (tmpNum != 0)
{
resultStr.Append(tmpNum);
resultStr.Append(littleCompany);
}
return resultStr.ToString();
}
#endregion
}
TextUtilUsageExample.cs(文本模块使用示例)
using UnityEngine;
/// <summary>
/// 文本模块完整使用示例
/// </summary>
public class TextUtilUsageExample : MonoBehaviour
{
void Start()
{
Debug.Log("=== 文本模块使用示例 ===");
// 示例1:解析配置表数据
Example_ParseConfigData();
// 示例2:UI显示格式化
Example_FormatUIDisplay();
// 示例3:时间倒计时显示
Example_CountdownDisplay();
// 示例4:排行榜数据显示
Example_RankDisplay();
}
#region 示例1:解析配置表数据
/// <summary>
/// 解析配置表数据
/// </summary>
private void Example_ParseConfigData()
{
Debug.Log("--- 示例1:解析配置表数据 ---");
// 场景:从配置表中读取道具列表
// 格式:"道具ID,数量;道具ID,数量"
string itemData = "1,10;2,20;3,5;4,100";
TextUtil.SplitStrToIntArrTwice(itemData, 1, 2, (itemID, count) =>
{
Debug.Log($"道具ID: {itemID}, 数量: {count}");
// 实际使用:BackpackManager.AddItem(itemID, count);
});
}
#endregion
#region 示例2:UI显示格式化
/// <summary>
/// UI显示格式化
/// </summary>
private void Example_FormatUIDisplay()
{
Debug.Log("--- 示例2:UI显示格式化 ---");
// 场景1:显示玩家等级
int playerLevel = 15;
string levelStr = TextUtil.GetNumStr(playerLevel, 3);
Debug.Log($"玩家等级:{levelStr}"); // 015
// 场景2:显示金币数量(大数值)
int gold = 999999;
string goldStr = TextUtil.GetBigDataToString(gold);
Debug.Log($"玩家金币:{goldStr}"); // 99万9千
// 场景3:显示分数(带小数点)
float score = 95.6789f;
string scoreStr = TextUtil.GetDecimalStr(score, 2);
Debug.Log($"得分:{scoreStr}"); // 95.68
}
#endregion
#region 示例3:时间倒计时显示
/// <summary>
/// 时间倒计时显示
/// </summary>
private void Example_CountdownDisplay()
{
Debug.Log("--- 示例3:时间倒计时显示 ---");
// 场景1:标准格式
int totalSeconds = 3661;
string timeStr1 = TextUtil.SecondToHMS(totalSeconds);
Debug.Log($"总时间:{timeStr1}"); // 1时1分1秒
// 场景2:00:00:00格式
string timeStr2 = TextUtil.SecondToHMS2(totalSeconds);
Debug.Log($"总时间:{timeStr2}"); // 01:01:01
// 场景3:忽略0值
int seconds = 65;
string timeStr3 = TextUtil.SecondToHMS(seconds, true);
Debug.Log($"简略时间:{timeStr3}"); // 1分5秒
}
#endregion
#region 示例4:排行榜数据显示
/// <summary>
/// 排行榜数据显示
/// </summary>
private void Example_RankDisplay()
{
Debug.Log("--- 示例4:排行榜数据显示 ---");
// 模拟排行榜数据
int[] levels = { 150, 99, 87, 1, 5 };
int[] scores = { 999999999, 12345678, 999999, 1000, 50000 };
for (int i = 0; i < levels.Length; i++)
{
string rankStr = TextUtil.GetNumStr(i + 1, 2);
string levelStr = TextUtil.GetNumStr(levels[i], 3);
string scoreStr = TextUtil.GetBigDataToString(scores[i]);
Debug.Log($"排名:{rankStr} | 等级:{levelStr} | 分数:{scoreStr}");
// 输出:
// 排名:01 | 等级:150 | 分数:99亿9千万
// 排名:02 | 等级:099 | 分数:1234万5千
// 排名:03 | 等级:087 | 分数:99万9千
// 排名:04 | 等级:001 | 分数:1000
// 排名:05 | 等级:005 | 分数:5万
}
}
#endregion
}
TextUtilCompleteTest.cs(文本模块完整测试)
using UnityEngine;
/// <summary>
/// 文本模块完整测试
/// </summary>
public class TextUtilCompleteTest : MonoBehaviour
{
void Start()
{
Debug.Log("=== 文本模块完整测试 ===");
// 测试字符串拆分
TestStringSplit();
// 测试数字格式化
TestNumberFormat();
// 测试时间转换
TestTimeConversion();
// 测试大数值转换
TestBigNumberConversion();
}
/// <summary>
/// 测试字符串拆分
/// </summary>
private void TestStringSplit()
{
Debug.Log("--- 测试字符串拆分 ---");
// 测试1:基础拆分
string[] result1 = TextUtil.SplitStr("1;2;3", 1);
Debug.Log($"基础拆分:{string.Join(", ", result1)}"); // 1, 2, 3
// 测试2:中文符号兼容
int[] result2 = TextUtil.SplitStrToIntArr("10,20,30", 2);
Debug.Log($"中文符号:{string.Join(", ", result2)}"); // 10, 20, 30
// 测试3:空字符串处理
string[] result3 = TextUtil.SplitStr("", 1);
Debug.Log($"空字符串长度:{result3.Length}"); // 0
}
/// <summary>
/// 测试数字格式化
/// </summary>
private void TestNumberFormat()
{
Debug.Log("--- 测试数字格式化 ---");
// 测试前补0
Debug.Log($"GetNumStr(1, 4) = {TextUtil.GetNumStr(1, 4)}"); // 0001
Debug.Log($"GetNumStr(100, 3) = {TextUtil.GetNumStr(100, 3)}"); // 100
// 测试小数
Debug.Log($"GetDecimalStr(3.14, 2) = {TextUtil.GetDecimalStr(3.14f, 2)}"); // 3.14
}
/// <summary>
/// 测试时间转换
/// </summary>
private void TestTimeConversion()
{
Debug.Log("--- 测试时间转换 ---");
// 测试标准格式
Debug.Log($"SecondToHMS(3661) = {TextUtil.SecondToHMS(3661)}"); // 1时1分1秒
Debug.Log($"SecondToHMS2(3661) = {TextUtil.SecondToHMS2(3661)}"); // 01:01:01
// 测试忽略0
Debug.Log($"SecondToHMS(65, true) = {TextUtil.SecondToHMS(65, true)}"); // 1分5秒
}
/// <summary>
/// 测试大数值转换
/// </summary>
private void TestBigNumberConversion()
{
Debug.Log("--- 测试大数值转换 ---");
Debug.Log($"GetBigDataToString(1245000000) = {TextUtil.GetBigDataToString(1245000000)}"); // 12亿4千万
Debug.Log($"GetBigDataToString(5689523) = {TextUtil.GetBigDataToString(5689523)}"); // 568万9千
Debug.Log($"GetBigDataToString(9999) = {TextUtil.GetBigDataToString(9999)}"); // 9999
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com