forked from moyin/whale-town-front
- GameManager.gd:游戏状态管理注释 - NetworkManager.gd:网络请求管理注释 - SceneManager.gd:场景切换管理注释 - StringUtils.gd:字符串工具函数注释 按照docs注释规范,添加文件头、函数说明、参数描述和使用示例 方便协同开发者快速理解和调用
386 lines
10 KiB
GDScript
386 lines
10 KiB
GDScript
class_name StringUtils
|
||
|
||
# ============================================================================
|
||
# StringUtils.gd - 字符串工具类
|
||
# ============================================================================
|
||
# 静态工具类,提供常用的字符串处理功能
|
||
#
|
||
# 核心功能:
|
||
# - 输入验证(邮箱、用户名、密码)
|
||
# - 字符串格式化和转换
|
||
# - 时间格式化和相对时间计算
|
||
# - 文件大小格式化
|
||
#
|
||
# 使用方式:
|
||
# var is_valid = StringUtils.is_valid_email("user@example.com")
|
||
# var formatted_time = StringUtils.format_utc_to_local_time(utc_string)
|
||
#
|
||
# 注意事项:
|
||
# - 所有方法都是静态的,无需实例化
|
||
# - 验证方法返回布尔值或包含详细信息的字典
|
||
# - 时间处理方法支持UTC到本地时间的转换
|
||
# ============================================================================
|
||
|
||
# ============ 输入验证方法 ============
|
||
|
||
# 验证邮箱格式
|
||
#
|
||
# 参数:
|
||
# email: String - 待验证的邮箱地址
|
||
#
|
||
# 返回值:
|
||
# bool - true表示格式正确,false表示格式错误
|
||
#
|
||
# 验证规则:
|
||
# - 必须包含@符号
|
||
# - @前后都必须有内容
|
||
# - 域名部分必须包含至少一个点
|
||
# - 顶级域名至少2个字符
|
||
#
|
||
# 使用示例:
|
||
# if StringUtils.is_valid_email("user@example.com"):
|
||
# print("邮箱格式正确")
|
||
static func is_valid_email(email: String) -> bool:
|
||
var regex = RegEx.new()
|
||
regex.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")
|
||
return regex.search(email) != null
|
||
|
||
# 验证用户名格式
|
||
#
|
||
# 参数:
|
||
# username: String - 待验证的用户名
|
||
#
|
||
# 返回值:
|
||
# bool - true表示格式正确,false表示格式错误
|
||
#
|
||
# 验证规则:
|
||
# - 只能包含字母、数字、下划线
|
||
# - 长度不能为空且不超过50个字符
|
||
# - 不能包含空格或特殊字符
|
||
#
|
||
# 使用示例:
|
||
# if StringUtils.is_valid_username("user_123"):
|
||
# print("用户名格式正确")
|
||
static func is_valid_username(username: String) -> bool:
|
||
# 检查长度
|
||
if username.is_empty() or username.length() > 50:
|
||
return false
|
||
|
||
# 检查字符组成
|
||
var regex = RegEx.new()
|
||
regex.compile("^[a-zA-Z0-9_]+$")
|
||
return regex.search(username) != null
|
||
|
||
# 验证密码强度
|
||
#
|
||
# 参数:
|
||
# password: String - 待验证的密码
|
||
#
|
||
# 返回值:
|
||
# Dictionary - 包含验证结果的详细信息
|
||
# {
|
||
# "valid": bool, # 是否符合最低要求
|
||
# "message": String, # 验证结果消息
|
||
# "strength": int # 强度等级 (1-4)
|
||
# }
|
||
#
|
||
# 验证规则:
|
||
# - 最少8位,最多128位
|
||
# - 必须包含字母和数字
|
||
# - 强度评级:包含字母(+1)、数字(+1)、特殊字符(+1)、长度>=12(+1)
|
||
#
|
||
# 使用示例:
|
||
# var result = StringUtils.validate_password_strength("MyPass123!")
|
||
# if result.valid:
|
||
# print("密码强度: ", result.message)
|
||
static func validate_password_strength(password: String) -> Dictionary:
|
||
var result = {"valid": false, "message": "", "strength": 0}
|
||
|
||
# 检查长度限制
|
||
if password.length() < 8:
|
||
result.message = "密码长度至少8位"
|
||
return result
|
||
|
||
if password.length() > 128:
|
||
result.message = "密码长度不能超过128位"
|
||
return result
|
||
|
||
# 检查字符类型
|
||
var has_letter = false # 是否包含字母
|
||
var has_digit = false # 是否包含数字
|
||
var has_special = false # 是否包含特殊字符
|
||
|
||
for i in range(password.length()):
|
||
var character = password[i]
|
||
if character >= 'a' and character <= 'z' or character >= 'A' and character <= 'Z':
|
||
has_letter = true
|
||
elif character >= '0' and character <= '9':
|
||
has_digit = true
|
||
elif character in "!@#$%^&*()_+-=[]{}|;:,.<>?":
|
||
has_special = true
|
||
|
||
# 计算强度等级
|
||
var strength = 0
|
||
if has_letter:
|
||
strength += 1
|
||
if has_digit:
|
||
strength += 1
|
||
if has_special:
|
||
strength += 1
|
||
if password.length() >= 12:
|
||
strength += 1
|
||
|
||
result.strength = strength
|
||
|
||
# 检查最低要求
|
||
if not (has_letter and has_digit):
|
||
result.message = "密码必须包含字母和数字"
|
||
return result
|
||
|
||
# 密码符合要求
|
||
result.valid = true
|
||
result.message = "密码强度: " + ["弱", "中", "强", "很强"][min(strength - 1, 3)]
|
||
return result
|
||
|
||
# ============ 字符串格式化方法 ============
|
||
|
||
# 截断字符串
|
||
#
|
||
# 参数:
|
||
# text: String - 原始字符串
|
||
# max_length: int - 最大长度
|
||
# suffix: String - 截断后缀(默认为"...")
|
||
#
|
||
# 返回值:
|
||
# String - 截断后的字符串
|
||
#
|
||
# 功能:
|
||
# - 如果字符串长度超过限制,截断并添加后缀
|
||
# - 如果字符串长度未超过限制,返回原字符串
|
||
#
|
||
# 使用示例:
|
||
# var short_text = StringUtils.truncate("这是一个很长的文本", 10, "...")
|
||
# # 结果: "这是一个很长..."
|
||
static func truncate(text: String, max_length: int, suffix: String = "...") -> String:
|
||
if text.length() <= max_length:
|
||
return text
|
||
return text.substr(0, max_length - suffix.length()) + suffix
|
||
|
||
# 首字母大写
|
||
#
|
||
# 参数:
|
||
# text: String - 原始字符串
|
||
#
|
||
# 返回值:
|
||
# String - 首字母大写的字符串
|
||
#
|
||
# 使用示例:
|
||
# var capitalized = StringUtils.capitalize_first("hello world")
|
||
# # 结果: "Hello world"
|
||
static func capitalize_first(text: String) -> String:
|
||
if text.is_empty():
|
||
return text
|
||
return text[0].to_upper() + text.substr(1)
|
||
|
||
# 转换为标题格式
|
||
#
|
||
# 参数:
|
||
# text: String - 原始字符串
|
||
#
|
||
# 返回值:
|
||
# String - 每个单词首字母大写的字符串
|
||
#
|
||
# 功能:
|
||
# - 将每个单词的首字母转换为大写
|
||
# - 其余字母转换为小写
|
||
# - 以空格分隔单词
|
||
#
|
||
# 使用示例:
|
||
# var title = StringUtils.to_title_case("hello world game")
|
||
# # 结果: "Hello World Game"
|
||
static func to_title_case(text: String) -> String:
|
||
var words = text.split(" ")
|
||
var result = []
|
||
for word in words:
|
||
if not word.is_empty():
|
||
result.append(capitalize_first(word.to_lower()))
|
||
return " ".join(result)
|
||
|
||
# 移除HTML标签
|
||
#
|
||
# 参数:
|
||
# html: String - 包含HTML标签的字符串
|
||
#
|
||
# 返回值:
|
||
# String - 移除HTML标签后的纯文本
|
||
#
|
||
# 功能:
|
||
# - 使用正则表达式移除所有HTML标签
|
||
# - 保留标签之间的文本内容
|
||
#
|
||
# 使用示例:
|
||
# var plain_text = StringUtils.strip_html_tags("<p>Hello <b>World</b></p>")
|
||
# # 结果: "Hello World"
|
||
static func strip_html_tags(html: String) -> String:
|
||
var regex = RegEx.new()
|
||
regex.compile("<[^>]*>")
|
||
return regex.sub(html, "", true)
|
||
|
||
# 格式化文件大小
|
||
#
|
||
# 参数:
|
||
# bytes: int - 文件大小(字节)
|
||
#
|
||
# 返回值:
|
||
# String - 格式化后的文件大小字符串
|
||
#
|
||
# 功能:
|
||
# - 自动选择合适的单位(B, KB, MB, GB, TB)
|
||
# - 保留一位小数(除了字节)
|
||
# - 使用1024作为换算基数
|
||
#
|
||
# 使用示例:
|
||
# var size_text = StringUtils.format_file_size(1536)
|
||
# # 结果: "1.5 KB"
|
||
static func format_file_size(bytes: int) -> String:
|
||
var units = ["B", "KB", "MB", "GB", "TB"]
|
||
var size = float(bytes)
|
||
var unit_index = 0
|
||
|
||
# 自动选择合适的单位
|
||
while size >= 1024.0 and unit_index < units.size() - 1:
|
||
size /= 1024.0
|
||
unit_index += 1
|
||
|
||
# 格式化输出
|
||
if unit_index == 0:
|
||
return str(int(size)) + " " + units[unit_index]
|
||
else:
|
||
return "%.1f %s" % [size, units[unit_index]]
|
||
|
||
# ============ 时间处理方法 ============
|
||
|
||
# 将UTC时间字符串转换为本地时间显示
|
||
#
|
||
# 参数:
|
||
# utc_time_str: String - UTC时间字符串(格式: 2025-12-25T11:23:52.175Z)
|
||
#
|
||
# 返回值:
|
||
# String - 格式化的本地时间字符串
|
||
#
|
||
# 功能:
|
||
# - 解析ISO 8601格式的UTC时间
|
||
# - 转换为本地时区时间
|
||
# - 格式化为易读的中文时间格式
|
||
#
|
||
# 使用示例:
|
||
# var local_time = StringUtils.format_utc_to_local_time("2025-12-25T11:23:52.175Z")
|
||
# # 结果: "2025年12月25日 19:23:52" (假设本地时区为UTC+8)
|
||
static func format_utc_to_local_time(utc_time_str: String) -> String:
|
||
# 解析UTC时间字符串 (格式: 2025-12-25T11:23:52.175Z)
|
||
var regex = RegEx.new()
|
||
regex.compile("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})")
|
||
var result = regex.search(utc_time_str)
|
||
|
||
if result == null:
|
||
return utc_time_str # 如果解析失败,返回原字符串
|
||
|
||
# 提取时间组件
|
||
var year = int(result.get_string(1))
|
||
var month = int(result.get_string(2))
|
||
var day = int(result.get_string(3))
|
||
var hour = int(result.get_string(4))
|
||
var minute = int(result.get_string(5))
|
||
var second = int(result.get_string(6))
|
||
|
||
# 创建UTC时间字典
|
||
var utc_dict = {
|
||
"year": year,
|
||
"month": month,
|
||
"day": day,
|
||
"hour": hour,
|
||
"minute": minute,
|
||
"second": second
|
||
}
|
||
|
||
# 转换为Unix时间戳(UTC)
|
||
var utc_timestamp = Time.get_unix_time_from_datetime_dict(utc_dict)
|
||
|
||
# 获取本地时间(Godot会自动处理时区转换)
|
||
var local_dict = Time.get_datetime_dict_from_unix_time(utc_timestamp)
|
||
|
||
# 格式化为易读的本地时间
|
||
return "%04d年%02d月%02d日 %02d:%02d:%02d" % [
|
||
local_dict.year,
|
||
local_dict.month,
|
||
local_dict.day,
|
||
local_dict.hour,
|
||
local_dict.minute,
|
||
local_dict.second
|
||
]
|
||
|
||
# 获取相对时间描述
|
||
#
|
||
# 参数:
|
||
# utc_time_str: String - UTC时间字符串
|
||
#
|
||
# 返回值:
|
||
# String - 相对时间描述(如"5分钟后"、"2小时30分钟后")
|
||
#
|
||
# 功能:
|
||
# - 计算指定时间与当前时间的差值
|
||
# - 返回人性化的相对时间描述
|
||
# - 支持秒、分钟、小时的组合显示
|
||
#
|
||
# 使用示例:
|
||
# var relative_time = StringUtils.get_relative_time_until("2025-12-25T12:00:00Z")
|
||
# # 结果: "30分钟后" 或 "现在可以重试"
|
||
static func get_relative_time_until(utc_time_str: String) -> String:
|
||
# 解析UTC时间字符串
|
||
var regex = RegEx.new()
|
||
regex.compile("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})")
|
||
var result = regex.search(utc_time_str)
|
||
|
||
if result == null:
|
||
return "时间格式错误"
|
||
|
||
# 提取时间组件
|
||
var year = int(result.get_string(1))
|
||
var month = int(result.get_string(2))
|
||
var day = int(result.get_string(3))
|
||
var hour = int(result.get_string(4))
|
||
var minute = int(result.get_string(5))
|
||
var second = int(result.get_string(6))
|
||
|
||
# 创建UTC时间字典
|
||
var utc_dict = {
|
||
"year": year,
|
||
"month": month,
|
||
"day": day,
|
||
"hour": hour,
|
||
"minute": minute,
|
||
"second": second
|
||
}
|
||
|
||
# 转换为Unix时间戳
|
||
var target_timestamp = Time.get_unix_time_from_datetime_dict(utc_dict)
|
||
var current_timestamp = Time.get_unix_time_from_system()
|
||
|
||
# 计算时间差(秒)
|
||
var diff_seconds = target_timestamp - current_timestamp
|
||
|
||
# 格式化相对时间
|
||
if diff_seconds <= 0:
|
||
return "现在可以重试"
|
||
elif diff_seconds < 60:
|
||
return "%d秒后" % diff_seconds
|
||
elif diff_seconds < 3600:
|
||
var minutes = int(diff_seconds / 60)
|
||
return "%d分钟后" % minutes
|
||
else:
|
||
var hours = int(diff_seconds / 3600)
|
||
var minutes = int((diff_seconds % 3600) / 60)
|
||
if minutes > 0:
|
||
return "%d小时%d分钟后" % [hours, minutes]
|
||
else:
|
||
return "%d小时后" % hours |