文件系统子系统(CategoryFilesystem)
SDL 提供一套用于检查和操作系统文件系统的 API,涵盖了目录相关的大部分常用操作(实际的文件读写 I/O 功能由 CategoryIOStream 和 CategoryAsyncIO 模块提供)。
该模块可回答各类路径相关的核心问题:
- 应用程序的数据目录在哪里?
SDL_GetBasePath() - 哪里可以安全地写入文件?
SDL_GetPrefPath() - 系统预设文件夹(下载、桌面、音乐等)的路径是什么?
SDL_GetUserFolder() - 指定路径对应的文件/目录属性是什么?
SDL_GetPathInfo() - 某个文件夹下包含哪些文件/子目录?
SDL_EnumerateDirectory() - 按通配符匹配某个文件夹下的文件?
SDL_GlobDirectory() - 当前工作目录是什么?
SDL_GetCurrentDirectory()
此外,SDL 还提供了操作目录树的函数,支持文件/目录的重命名、删除、复制等操作。
函数
- SDL_CopyFile:复制文件(支持跨目录复制,保留文件属性)
- SDL_CreateDirectory:创建目录(支持创建单层目录,失败时返回具体错误)
- SDL_EnumerateDirectory:枚举目录下的所有项(文件/子目录),通过回调函数处理每个项
- SDL_GetBasePath:获取应用程序的基础目录(二进制文件所在目录,末尾带路径分隔符)
- SDL_GetCurrentDirectory:获取当前工作目录(进程的当前工作路径)
- SDL_GetPathInfo:获取指定路径的详细信息(类型、大小、修改时间等,存储在 SDL_PathInfo 中)
- SDL_GetPrefPath:获取应用程序的偏好设置目录(可安全写入的目录,按厂商/应用名划分)
- SDL_GetUserFolder:获取系统预设的用户文件夹路径(桌面、下载、音乐等)
- SDL_GlobDirectory:按通配符匹配枚举目录下的文件(支持 *、? 等通配符)
- SDL_RemovePath:删除指定路径(文件/空目录,非空目录删除失败)
- SDL_RenamePath:重命名/移动文件/目录(支持跨目录移动,目标路径已存在则失败)
数据类型
- SDL_EnumerateDirectoryCallback:目录枚举回调函数类型(处理每个枚举到的文件/目录项)
- SDL_GlobFlags:通配符枚举标志类型(控制 Glob 匹配的行为,如大小写敏感、递归等)
结构体
- SDL_PathInfo:路径信息结构体(包含路径类型、大小、创建时间、修改时间等属性)
枚举
- SDL_EnumerationResult:目录枚举结果枚举(控制是否继续枚举,如继续、停止、跳过当前项)
- SDL_Folder:系统预设文件夹类型枚举(桌面、下载、音乐、文档、图片等)
- SDL_PathType:路径类型枚举(文件、目录、符号链接、不存在等)
FreeBASIC 示例代码
' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库)
#Include "SDL.bi"
' 补充枚举定义
Enum SDL_PathType
SDL_PATH_TYPE_NONE = 0 ' 路径不存在
SDL_PATH_TYPE_FILE = 1 ' 普通文件
SDL_PATH_TYPE_DIRECTORY = 2 ' 目录
SDL_PATH_TYPE_SYMLINK = 3 ' 符号链接
SDL_PATH_TYPE_OTHER = 4 ' 其他类型
End Enum
Enum SDL_Folder
SDL_FOLDER_DESKTOP = 0 ' 桌面
SDL_FOLDER_DOWNLOADS = 1 ' 下载
SDL_FOLDER_DOCUMENTS = 2 ' 文档
SDL_FOLDER_MUSIC = 3 ' 音乐
SDL_FOLDER_PICTURES = 4 ' 图片
SDL_FOLDER_VIDEOS = 5 ' 视频
SDL_FOLDER_APPDATA = 6 ' 应用数据
End Enum
Enum SDL_EnumerationResult
SDL_ENUMERATION_CONTINUE = 0 ' 继续枚举
SDL_ENUMERATION_STOP = 1 ' 停止枚举
SDL_ENUMERATION_SKIP = 2 ' 跳过当前项
End Enum
' 补充结构体定义
Type SDL_PathInfo
type_ As SDL_PathType ' 路径类型
size As ULLong ' 文件大小(字节),目录为0
created As SDL_Time ' 创建时间(纳秒时间戳)
modified As SDL_Time ' 修改时间(纳秒时间戳)
accessed As SDL_Time ' 访问时间(纳秒时间戳)
End Type
' 补充函数指针类型定义
Type SDL_EnumerateDirectoryCallback As Function CDecl (ByVal path As ZString Ptr, ByVal info As SDL_PathInfo Ptr, ByVal userdata As Any Ptr) As SDL_EnumerationResult
' 补充函数声明(FreeBASIC 绑定可能缺失)
Declare Function SDL_GetBasePath CDecl () As ZString Ptr
Declare Function SDL_GetPrefPath CDecl (ByVal org As ZString Ptr, ByVal app As ZString Ptr) As ZString Ptr
Declare Function SDL_GetUserFolder CDecl (ByVal folder As SDL_Folder) As ZString Ptr
Declare Function SDL_GetCurrentDirectory CDecl () As ZString Ptr
Declare Function SDL_GetPathInfo CDecl (ByVal path As ZString Ptr, ByVal info As SDL_PathInfo Ptr) As Integer
Declare Function SDL_EnumerateDirectory CDecl (ByVal dir As ZString Ptr, ByVal callback As SDL_EnumerateDirectoryCallback, ByVal userdata As Any Ptr) As Integer
Declare Function SDL_CreateDirectory CDecl (ByVal path As ZString Ptr) As Integer
Declare Function SDL_CopyFile CDecl (ByVal src As ZString Ptr, ByVal dst As ZString Ptr) As Integer
Declare Function SDL_RenamePath CDecl (ByVal oldpath As ZString Ptr, ByVal newpath As ZString Ptr) As Integer
Declare Function SDL_RemovePath CDecl (ByVal path As ZString Ptr) As Integer
' 目录枚举回调函数:打印每个文件/目录信息
Function EnumDirCallback CDecl (ByVal path As ZString Ptr, ByVal info As SDL_PathInfo Ptr, ByVal userdata As Any Ptr) As SDL_EnumerationResult
Dim As ZString * 20 typeStr
Select Case info->type_
Case SDL_PATH_TYPE_FILE: typeStr = "文件"
Case SDL_PATH_TYPE_DIRECTORY: typeStr = "目录"
Case SDL_PATH_TYPE_SYMLINK: typeStr = "符号链接"
Case Else: typeStr = "其他类型"
End Select
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " %s - [%s] 大小:%llu 字节", *path, typeStr, info->size)
' 继续枚举所有项
Return SDL_ENUMERATION_CONTINUE
End Function
' 打印路径信息
Sub PrintPathInfo(ByVal path As ZString Ptr, ByVal title As ZString Ptr)
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "=== %s ===", title)
Dim As SDL_PathInfo info
If (SDL_GetPathInfo(path, @info) = 0) Then
Dim As ZString * 20 typeStr
Select Case info.type_
Case SDL_PATH_TYPE_NONE: typeStr = "不存在"
Case SDL_PATH_TYPE_FILE: typeStr = "普通文件"
Case SDL_PATH_TYPE_DIRECTORY: typeStr = "目录"
Case SDL_PATH_TYPE_SYMLINK: typeStr = "符号链接"
Case SDL_PATH_TYPE_OTHER: typeStr = "其他类型"
End Select
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 路径:%s", *path)
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 类型:%s", typeStr)
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 大小:%llu 字节", info.size)
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 创建时间:%llu ns", info.created)
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 修改时间:%llu ns", info.modified)
Else
SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "获取路径信息失败:%s", SDL_GetError())
End If
End Sub
' 主程序
Sub Main()
' 初始化 SDL(Filesystem 模块无需显式初始化子系统)
If (SDL_Init(0) < 0) Then
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError())
Exit Sub
End If
' ========== 1. 获取系统/应用路径 ==========
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "【1. 获取系统/应用路径】")
' 应用基础目录
Dim As ZString Ptr basePath = SDL_GetBasePath()
If (basePath <> NULL) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "应用基础目录:%s", *basePath)
SDL_free(basePath) ' 必须释放 SDL 分配的字符串
End If
' 应用偏好设置目录(厂商名/应用名)
Dim As ZString Ptr prefPath = SDL_GetPrefPath("SDL_Example", "FilesystemDemo")
If (prefPath <> NULL) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "应用偏好设置目录:%s", *prefPath)
End If
' 当前工作目录
Dim As ZString Ptr cwd = SDL_GetCurrentDirectory()
If (cwd <> NULL) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "当前工作目录:%s", *cwd)
SDL_free(cwd)
End If
' 系统预设用户文件夹
Dim As ZString * 20 folderNames(0 To 5) = {
"桌面", "下载", "文档", "音乐", "图片", "视频"
}
For i As Integer = 0 To 5
Dim As ZString Ptr folderPath = SDL_GetUserFolder(i)
If (folderPath <> NULL) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "%s 目录:%s", folderNames(i), *folderPath)
SDL_free(folderPath)
End If
Next
' ========== 2. 创建测试目录和文件 ==========
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, vbCrLf & "【2. 创建测试目录和文件】")
' 拼接测试目录路径(偏好设置目录下的 test_dir)
Dim As String testDirPath = *prefPath & "test_dir"
If (SDL_CreateDirectory(StrPtr(testDirPath)) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "创建测试目录成功:%s", testDirPath)
Else
SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "创建测试目录失败:%s", SDL_GetError())
End If
' 创建测试文件(FreeBASIC 原生文件操作)
Dim As String testFilePath = testDirPath & "/test.txt"
Dim As Integer fileHandle = FreeFile()
If (Open(testFilePath For Output As #fileHandle) = 0) Then
Print #fileHandle, "SDL Filesystem 测试文件"
Close #fileHandle
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "创建测试文件成功:%s", testFilePath)
End If
' ========== 3. 获取路径信息 ==========
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, vbCrLf & "【3. 获取路径信息】")
PrintPathInfo(StrPtr(testFilePath), "测试文件信息")
PrintPathInfo(StrPtr(testDirPath), "测试目录信息")
' ========== 4. 枚举目录内容 ==========
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, vbCrLf & "【4. 枚举测试目录内容】")
If (SDL_EnumerateDirectory(StrPtr(testDirPath), @EnumDirCallback, NULL) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "目录枚举完成")
Else
SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "目录枚举失败:%s", SDL_GetError())
End If
' ========== 5. 复制/重命名/删除文件 ==========
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, vbCrLf & "【5. 文件操作:复制/重命名/删除】")
' 复制文件
Dim As String copyFilePath = testDirPath & "/test_copy.txt"
If (SDL_CopyFile(StrPtr(testFilePath), StrPtr(copyFilePath)) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "复制文件成功:%s -> %s", testFilePath, copyFilePath)
Else
SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "复制文件失败:%s", SDL_GetError())
End If
' 重命名文件
Dim As String renameFilePath = testDirPath & "/test_renamed.txt"
If (SDL_RenamePath(StrPtr(copyFilePath), StrPtr(renameFilePath)) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "重命名文件成功:%s -> %s", copyFilePath, renameFilePath)
Else
SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "重命名文件失败:%s", SDL_GetError())
End If
' 删除文件
If (SDL_RemovePath(StrPtr(testFilePath)) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "删除文件成功:%s", testFilePath)
End If
If (SDL_RemovePath(StrPtr(renameFilePath)) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "删除文件成功:%s", renameFilePath)
End If
' 删除目录(必须为空)
If (SDL_RemovePath(StrPtr(testDirPath)) = 0) Then
SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "删除测试目录成功:%s", testDirPath)
Else
SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "删除测试目录失败(可能非空):%s", SDL_GetError())
End If
' 释放内存
If (prefPath <> NULL) Then
SDL_free(prefPath)
End If
' 清理 SDL
SDL_Quit()
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出")
End Sub
' 运行主程序
Main()
核心知识点补充
-
路径管理核心规则:
SDL_GetBasePath()返回的路径末尾自带路径分隔符(/ 或 \),拼接子路径时无需额外添加;SDL_GetPrefPath()需要传入厂商名和应用名,返回的目录是系统推荐的可写入目录(跨平台兼容);- SDL 分配的路径字符串(如
SDL_GetBasePath()返回值)必须用SDL_free()释放,避免内存泄漏。
-
路径类型与操作限制:
SDL_RemovePath()只能删除文件或空目录,非空目录需先删除内部文件;SDL_CreateDirectory()仅创建单层目录,创建多级目录需逐层调用;SDL_RenamePath()支持跨目录移动文件,但目标路径不能已存在。
-
目录枚举回调函数:
- 返回
SDL_ENUMERATION_CONTINUE:继续枚举下一个项; - 返回
SDL_ENUMERATION_STOP:立即停止枚举; - 返回
SDL_ENUMERATION_SKIP:跳过当前项,继续枚举下一个; - 回调函数中可通过
SDL_PathInfo判断项的类型(文件/目录),实现差异化处理。
- 返回
-
系统预设文件夹兼容性:
SDL_GetUserFolder()返回的路径因操作系统而异:- Windows:C:\Users\<用户名>\Desktop/Downloads 等;
- macOS:/Users/<用户名>/Desktop/Downloads 等;
- Linux:/home/<用户名>/Desktop/Downloads 等;
- 部分平台可能不支持某些文件夹类型,返回 NULL,需做空指针判断。
总结
-
核心优势:
- 提供跨平台统一的文件系统操作接口,屏蔽不同系统的路径格式、目录结构差异;
- 自动适配系统的可写入目录、用户预设文件夹,避免手动拼接路径导致的兼容性问题;
- 内置路径信息查询、目录枚举、文件操作等完整功能,无需依赖平台原生 API;
- 内存管理清晰,SDL 分配的字符串需用
SDL_free()释放,符合 SDL 内存管理规范。
-
使用建议:
- 优先使用
SDL_GetPrefPath()存储应用配置文件,而非自定义路径,确保跨平台可写入; - 枚举目录时通过回调函数处理每个项,避免一次性加载大量文件导致内存占用过高;
- 所有文件/目录操作后检查返回值,失败时通过
SDL_GetError()获取具体错误信息; - 拼接路径时注意路径分隔符,优先使用 SDL 返回的带分隔符路径,避免手动拼接出错。
- 优先使用
-
关键接口:
- 路径获取:
SDL_GetBasePath()/SDL_GetPrefPath()/SDL_GetUserFolder()/SDL_GetCurrentDirectory(); - 路径信息:
SDL_GetPathInfo(); - 目录操作:
SDL_CreateDirectory()/SDL_EnumerateDirectory(); - 文件操作:
SDL_CopyFile()/SDL_RenamePath()/SDL_RemovePath()。
- 路径获取:
评论一下?