import t, c from stdint import * import string import memhub import platmacro # 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化) _mbuddy: memhub.MemBuddy | t.CPtr # ============================================================================= # 路径分隔符 # ============================================================================= def sep() -> t.CChar: """返回当前平台的路径分隔符(Windows: \\,POSIX: /)""" if platmacro.IS_WINDOWS: return '\\' return '/' def _is_sep(ch: t.CChar) -> bool: """判断字符是否为路径分隔符(/ 或 \\,跨平台识别)""" return ch == '/' or ch == '\\' # ============================================================================= # 内部辅助函数 # ============================================================================= def _dup_str(s: str) -> str: """复制字符串(通过全局 MBuddy 分配内存)""" if s == None: return None length: t.CSizeT = len(s) buf: bytes = _mbuddy.alloc(length + 1) if buf == None: return None i: t.CSizeT = 0 for ch in s: buf[i] = ch i += 1 buf[length] = 0 return str(buf) def _substr(s: str, start: t.CSizeT, end: t.CSizeT) -> str: """提取子串 s[start:end](通过全局 MBuddy 分配内存)""" if s == None: return None if start >= end: return _dup_str("") result_len: t.CSizeT = end - start buf: bytes = _mbuddy.alloc(result_len + 1) if buf == None: return None i: t.CSizeT = 0 k: t.CSizeT = start while k < end: buf[i] = s[k] i += 1 k += 1 buf[i] = 0 return str(buf) # ============================================================================= # 路径判断 # ============================================================================= def isabs(path: str) -> bool: """判断路径是否为绝对路径。 POSIX: 以 / 开头。 Windows: 以 \\ 开头(UNC)或 X:\\ / X:/ 开头(盘符)。""" if path == None: return False if path[0] == 0: return False if path[0] == '/' or path[0] == '\\': return True if platmacro.IS_WINDOWS: # 检查 X:\ 或 X:/ 模式 c0: t.CChar = path[0] if (c0 >= 'A' and c0 <= 'Z') or (c0 >= 'a' and c0 <= 'z'): if path[1] == ':': if path[2] == '\\' or path[2] == '/': return True return False # ============================================================================= # 路径拼接与分割 # ============================================================================= def join(path1: str, path2: str) -> str: """拼接两个路径。如果 path2 是绝对路径,返回 path2 的副本。""" if path1 == None or path1[0] == 0: return _dup_str(path2) if path2 == None or path2[0] == 0: return _dup_str(path1) if isabs(path2): return _dup_str(path2) len1: t.CSizeT = len(path1) len2: t.CSizeT = len(path2) # 检查 path1 是否以分隔符结尾 need_sep: bool = True if _is_sep(path1[len1 - 1]): need_sep = False total: t.CSizeT = len1 + len2 + 1 if need_sep: total += 1 buf: bytes = _mbuddy.alloc(total) if buf == None: return None # 复制 path1 i: t.CSizeT = 0 for ch in path1: buf[i] = ch i += 1 # 添加分隔符 if need_sep: buf[i] = sep() i += 1 # 复制 path2 for ch in path2: buf[i] = ch i += 1 buf[i] = 0 return str(buf) def basename(path: str) -> str: """返回路径的文件名部分(最后一个分隔符之后的内容)""" if path == None: return None length: t.CSizeT = len(path) if length == 0: return _dup_str("") # 从后向前找分隔符 i: t.CSizeT = length while i > 0: i -= 1 if _is_sep(path[i]): return _substr(path, i + 1, length) # 没有分隔符,返回整个路径的副本 return _dup_str(path) def dirname(path: str) -> str: """返回路径的目录部分(最后一个分隔符之前的内容)""" if path == None: return None length: t.CSizeT = len(path) if length == 0: return _dup_str("") # 从后向前找分隔符 i: t.CSizeT = length while i > 0: i -= 1 if _is_sep(path[i]): if i == 0: # 路径以分隔符开头,返回根目录 return _dup_str("/") return _substr(path, 0, i) # 没有分隔符,返回空字符串 return _dup_str("") # ============================================================================= # 扩展名处理 # ============================================================================= def splitext_name(path: str) -> str: """返回路径去除扩展名后的主名部分。 扩展名从最后一个 '.' 开始(该 '.' 不能是路径最后一个组件的第一个字符)。""" if path == None: return None length: t.CSizeT = len(path) if length == 0: return _dup_str("") # 从后向前找 '.',遇到分隔符停止 i: t.CSizeT = length while i > 0: i -= 1 if path[i] == '.': return _substr(path, 0, i) if _is_sep(path[i]): break # 没有扩展名,返回整个路径的副本 return _dup_str(path) def splitext_ext(path: str) -> str: """返回路径的扩展名部分(包含 '.')。无扩展名时返回空字符串。""" if path == None: return None length: t.CSizeT = len(path) if length == 0: return _dup_str("") # 从后向前找 '.',遇到分隔符停止 i: t.CSizeT = length while i > 0: i -= 1 if path[i] == '.': return _substr(path, i, length) if _is_sep(path[i]): break # 没有扩展名,返回空字符串 return _dup_str("") # ============================================================================= # 路径规范化 # ============================================================================= def normpath(path: str) -> str: """规范化路径:统一分隔符、消除多余的 . 和 .. 组件、合并连续分隔符。 注意:不解析符号链接,不调用系统 API。""" if path == None: return None length: t.CSizeT = len(path) if length == 0: return _dup_str(".") # 分配输出缓冲区(最坏情况:长度不变 + 前缀 /) buf: bytes = _mbuddy.alloc(length + 2) if buf == None: return None out_pos: t.CSizeT = 0 i: t.CSizeT = 0 s: str = sep() # 处理绝对路径前缀 is_absolute: bool = False if _is_sep(path[0]): is_absolute = True buf[out_pos] = s out_pos += 1 i = 1 # Windows 盘符前缀(X:\ 或 X:/) drive_prefix_len: t.CSizeT = 0 if platmacro.IS_WINDOWS: if length >= 3: c0: t.CChar = path[0] if (c0 >= 'A' and c0 <= 'Z') or (c0 >= 'a' and c0 <= 'z'): if path[1] == ':': if _is_sep(path[2]): buf[out_pos] = path[0] out_pos += 1 buf[out_pos] = ':' out_pos += 1 buf[out_pos] = s out_pos += 1 drive_prefix_len = 3 i = 3 is_absolute = True # 逐组件处理 while i < length: # 跳过连续分隔符 while i < length and _is_sep(path[i]): i += 1 if i >= length: break # 提取组件 comp_start: t.CSizeT = i while i < length and not _is_sep(path[i]): i += 1 comp_len: t.CSizeT = i - comp_start # 处理 "." 组件:跳过 if comp_len == 1 and path[comp_start] == '.': continue # 处理 ".." 组件:回退一级 if comp_len == 2 and path[comp_start] == '.' and path[comp_start + 1] == '.': if out_pos > drive_prefix_len: # 回退到上一个分隔符前 out_pos -= 1 while out_pos > drive_prefix_len and not _is_sep(buf[out_pos - 1]): out_pos -= 1 # 如果回退到了根目录前缀,不删除前缀 if out_pos == drive_prefix_len and is_absolute: out_pos = drive_prefix_len if drive_prefix_len == 0: # POSIX 根:保留 / buf[out_pos] = s out_pos += 1 continue # 普通组件:复制到输出 if out_pos > drive_prefix_len and not (drive_prefix_len == 0 and out_pos == 1 and is_absolute): buf[out_pos] = s out_pos += 1 elif out_pos == 0 and not is_absolute: pass # 第一个组件,不需要前导分隔符 elif is_absolute and out_pos == drive_prefix_len: # 已经有前导分隔符,不重复添加 pass else: buf[out_pos] = s out_pos += 1 j: t.CSizeT = comp_start while j < comp_start + comp_len: buf[out_pos] = path[j] out_pos += 1 j += 1 # 处理空结果 if out_pos == 0: buf[out_pos] = '.' out_pos += 1 buf[out_pos] = 0 return str(buf)