图像与设计
在 Dart 中跨平台安全处理文件路径时,用…
当需要拼接、拆分、检查、转换或重构文件路径、目录名、扩展名,或替换裸字符串路径操作时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Dart 中跨平台安全处理文件路径时通用
解决思路
- 不要将文件路径当作原始字符串处理:Windows 原生路径用反斜杠,macOS/Linux 用正斜杠,`.contains('foo/')`、`.startsWith('foo/')`、`.split('/')` 在 Windows 上会静默失败,`'$dir/$file'` 会注入正斜杠并在 `$dir` 以分隔符结尾时产生 `//`。
- 始终用 `p.split(path)` 拆分为段后再检查目录层级或段名,用 `p.join(...)` 拼接路径组件。
- 跨平台库(Windows + POSIX)中把各段分别传给 `p.join(dir, 'sub', 'file.json')`,让 package:path 插入 OS 原生分隔符。
- 仅面向 POSIX 的代码或需要静态子路径可 grep 时,优先使用 2 参数边界拼接 `p.join(home, '.local/share/app/bin/config.json')`,避免 `dart format` 换行破坏可搜索性,同时防止变量边界产生 `//`。
- 用 `p.normalize(path)` 做纯词法解析 `.` 和 `..`;去重目录路径或跨符号链接、相对根、大小写不敏感文件系统比较物理文件身份时用 `p.canonicalize(path)`。
- `<path>:<line>-<col>` 或 `<path>:<line>` 不是纯路径,先用正则 `RegExp(r'^(.*?):(\d+(?:-\d+)?)$')` 提取尾部后缀,再传给 package:path(否则 Windows 上 `Uri.parse` 会把 `C:` 当 scheme、`:line` 当端口)。
- 路径与 `Uri` 互转时用 `p.toUri(path)` 和 `p.fromUri(uri)`,不要用 `Uri.parse(path)` 或手动拼接字符串。
- 路径拼接:用 `p.join(dir, file)`,避免 `'$dir/$file'` 或 `'a/$b'`。
- 段匹配:用 `p.split(path).contains('foo')`,避免 `path.contains('foo/')`(Windows 反斜杠失败、部分子串误报)。
- 根与目录前缀:用 `p.split(path).first == 'foo'` 或 `p.isWithin('foo', path)`,避免 `path.startsWith('foo/')`(漏掉 `./foo/` 等相对前缀变体)。
- 文件扩展名:用 `p.extension(path) == '.wasm'`,避免 `path.endsWith('.wasm')`(会误匹配目录 `foo.wasm/` 或非扩展名后缀)。
- 扩展名切片与复合扩展名:用 `p.withoutExtension(path)` 和 `p.extension(path, 2)`,避免 `path.lastIndexOf('.')` 与手动 `substring`(在隐藏点文件 `.gitignore` 和复合扩展名 `.js.map`、`.tar.gz` 上出错)。
- POSIX 与 URL 路径转换:用 `p.posix.joinAll(p.split(path))` 或 `p.url.joinAll(p.split(path))`,避免 `path.replaceAll(r'\', '/')`(在根驱动器上失败并混淆 OS 与 POSIX/URL 上下文)。
- URI 转换:用 `p.toUri(path)` 和 `p.fromUri(uri)`,避免 `Uri.parse(path)` 和 `uri.path`(Windows 盘符失败、泄漏百分号编码如 `%20`)。
- 目录 basename 辅助函数:定义 `String canonicalDirName(Directory d) => p.basename(p.normalize(d.absolute.path));`,避免在各文件内联重复 `p.basename(p.normalize(dir.absolute.path))`。
- 桥接原生路径到 POSIX/Git/URL 上下文时,不要用 `.replaceAll('\\', '/')`;先 `p.relative(filePath, from: root)`,再用 `p.split(...)` 拆分、Dart 3 列表模式匹配检查段,最后用 `p.posix.joinAll(...)` 或 `p.url.joinAll(...)` 拼接。
- Git 路径与仓库元数据(tree 对象、`.gitignore`、`.gitattributes`、git 跟踪的符号链接)严格使用 POSIX 正斜杠;从原生路径程序化生成 `.gitignore` 条目、仓库清单或符号链接目标时,用 `p.posix.joinAll(p.split(relativePath))` 或 `p.posix.join(...)` 转换(反斜杠会被 Git 当作转义字符)。
- 使用 `package:file` 的代码库中,不要对 `File`/`Directory` 路径调用顶层 `p.*`(它绑定宿主 OS,在 Linux/macOS 上跑 `MemoryFileSystem(style: FileSystemStyle.windows)` 测试会按 `/` 拆分而失败);始终使用 `file.fileSystem.path` 的 `Context`,例如 `final pathContext = dir.fileSystem.path;` 再用 `pathContext.basename(d.path)`。
- 提取扩展名或插入内容哈希时避免手动 `.lastIndexOf('.')` 与 `.substring()` 算术;注意 `p.extension('main.dart.wasm', 2)` 会返回 `'.dart.wasm'`,因此先检查 `p.extension(filename, 2)` 是否匹配已知复合扩展名(或 `.endsWith('.map')`),否则回退到单级 `p.extension(filename)`,再计算 stem 并返回 `'$stem.$hash$ext'`。
- 重构审计清单:把 `'$dir/$file'` 换成 `p.join(dir, file)`;把 `.contains('dir/')`/`.startsWith('dir/')` 换成 `p.split(path)` 段检查或 `p.isWithin(parent, child)`;把 `.replaceAll(r'\', '/')` 换成 `p.posix.joinAll(p.split(path))`(或 `p.url.joinAll`);把 `.endsWith('.ext')` 换成 `p.extension(path) == '.ext'`;把手动点索引切片换成 `p.withoutExtension(path)` 和 `p.extension(path, [level])`;确认使用 `package:file` 的代码访问 `fileSystem.path` 而非全局 `p.*`;确保 Git 路径、`.gitignore` 条目和符号链接目标使用 `p.posix` 正斜杠。
适用边界 · 注意事项
- 不要用于 HTTP 网络 URI 路由。
- 不要用于数据库查询字符串。
- 不要用于非路径的字符串处理。