Nim 完全指南 / 15 文件与系统 I/O
第 15 章:文件与系统 I/O
15.1 文件读写
15.1.1 快速读写
# 读取整个文件
let content = readFile("data.txt")
echo content
# 写入文件
writeFile("output.txt", "Hello, Nim!\n")
# 追加写入
let f = open("output.txt", fmAppend)
f.writeLine("Appended line")
f.close()
15.1.2 逐行读取
# 逐行处理大文件
let f = open("data.txt")
defer: f.close()
var line: string
while f.readLine(line):
echo line
15.1.3 格式化读写
var f = open("numbers.txt", fmWrite)
for i in 1..10:
f.writeLine($i)
f.close()
# 读取所有行到序列
let lines = readFile("numbers.txt").splitLines()
for line in lines:
if line.len > 0:
echo parseInt(line)
15.2 文件系统操作
import std/os
# 文件信息
echo fileExists("data.txt") # true/false
echo dirExists("src") # true/false
echo getFileSize("data.txt") # 字节数
echo getLastModificationTime("data.txt")
# 创建/删除
createDir("tmp/nested") # 递归创建目录
removeDir("tmp") # 递归删除目录
removeFile("output.txt")
# 复制/移动
copyFile("source.txt", "dest.txt")
moveFile("old.txt", "new.txt")
# 遍历目录
for entry in walkDir("src"):
echo entry.kind, ": ", entry.path
for file in walkDirRec("src"):
echo file
# 临时文件
let (tmpFile, tmpHandle) = createTempFile("myapp", ".txt")
tmpHandle.writeLine("Temp data")
tmpHandle.close()
15.3 文件路径操作
import std/os
let path = "/home/user/documents/report.pdf"
echo extractFilename(path) # "report.pdf"
echo parentDir(path) # "/home/user/documents"
echo splitFile(path) # ("/home/user/documents", "report", ".pdf")
echo splitPath(path) # ("/home/user", "documents/report.pdf")
echo lastPathPart(path) # "report.pdf"
echo "/home" / "user" / "file.txt" # "/home/user/file.txt"
echo "file.txt".changeFileExt(".md") # "file.md"
echo expandTilde("~/documents") # 完整路径
echo getCurrentDir() # 当前工作目录
echo getAppFilename() # 可执行文件路径
15.4 环境变量
import std/os
# 读取环境变量
echo getEnv("HOME")
echo getEnv("PATH")
echo getEnv("MY_VAR", "default_value") # 带默认值
# 设置环境变量
putEnv("MY_APP_DEBUG", "true")
# 检查是否存在
echo existsEnv("HOME") # true
# 遍历所有环境变量
for key, value in envPairs():
echo &"{key}={value}"
15.5 命令行参数
import std/os
# 原始参数
echo paramCount() # 参数个数
echo paramStr(0) # 程序名
echo commandLineParams() # 所有参数(不含程序名)
# 高级参数解析
import std/parseopt
var
filename = ""
verbose = false
output = ""
for kind, key, val in getopt():
case kind
of cmdArgument:
filename = key
of cmdLongOption, cmdShortOption:
case key
of "verbose", "v": verbose = true
of "output", "o": output = val
of cmdEnd: discard
echo &"File: {filename}, verbose: {verbose}, output: {output}"
15.6 进程与系统调用
import std/osproc
# 执行外部命令
let (output, exitCode) = execCmdEx("ls -la")
echo output
echo "Exit code: ", exitCode
# 捕获输出
let result = execProcess("echo hello")
echo result
# 创建子进程
let p = startProcess("sleep", args = ["5"])
echo "PID: ", p.processID()
p.close()
15.7 实战示例
🏢 场景:日志文件轮转
import std/[os, times, strformat]
type RotatingLogger = object
basePath: string
maxSize: int64
maxFiles: int
proc newRotatingLogger(basePath: string, maxSize = 1024*1024, maxFiles = 5): RotatingLogger =
RotatingLogger(basePath: basePath, maxSize: maxSize, maxFiles: maxFiles)
proc rotateIfNeeded(rl: var RotatingLogger) =
if not fileExists(rl.basePath):
return
if getFileSize(rl.basePath) < rl.maxSize:
return
for i in countdown(rl.maxFiles - 1, 1):
let src = &"{rl.basePath}.{i}"
let dst = &"{rl.basePath}.{i + 1}"
if fileExists(src):
if i + 1 >= rl.maxFiles:
removeFile(src)
else:
moveFile(src, dst)
moveFile(rl.basePath, rl.basePath & ".1")
proc log(rl: var RotatingLogger, msg: string) =
rl.rotateIfNeeded()
let f = open(rl.basePath, fmAppend)
let ts = now().format("yyyy-MM-dd HH:mm:ss")
f.writeLine(&"[{ts}] {msg}")
f.close()
var logger = newRotatingLogger("app.log", maxSize = 1024*1024, maxFiles = 5)
for i in 1..100:
logger.log(&"Log entry {i}")
🏢 场景:配置文件管理
import std/[os, json, options, strutils]
type
Config = object
data: JsonNode
proc loadConfig(path: string): Option[Config] =
if not fileExists(path):
return none(Config)
try:
let content = readFile(path)
some(Config(data: parseJson(content)))
except:
none(Config)
proc saveConfig(cfg: Config, path: string) =
writeFile(path, cfg.data.pretty())
proc get(cfg: Config, path: string, default = ""): string =
var node = cfg.data
for part in path.split("."):
if node.hasKey(part):
node = node[part]
else:
return default
if node.kind == JString: node.getStr()
elif node.kind == JInt: $node.getInt()
else: default
# 使用
var cfg = loadConfig("config.json").get(Config(data: %*{}))
cfg.data = %*{
"server": {"host": "localhost", "port": 8080},
"debug": true
}
saveConfig(cfg, "config.json")
echo cfg.get("server.host") # localhost
echo cfg.get("server.port") # 8080
本章小结
| 操作 | 函数 | 模块 |
|---|---|---|
| 快速读写 | readFile, writeFile | system |
| 逐行读取 | f.readLine() | system |
| 文件操作 | fileExists, copyFile, moveFile | os |
| 目录操作 | createDir, walkDir, walkDirRec | os |
| 路径操作 | extractFilename, parentDir, / | os |
| 环境变量 | getEnv, putEnv | os |
| 命令行 | paramStr, getopt | os, parseopt |
| 进程 | execCmdEx, startProcess | osproc |
练习
- 编写一个文件搜索工具,支持通配符和正则
- 实现一个简单的 .env 文件解析器
- 创建一个带进度显示的文件复制工具
- 实现一个目录同步工具
扩展阅读
← 上一章:模块与包管理 | 下一章:并发编程 →