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("

Hello World

") # # 结果: "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