Julia 学习笔记——10. 输入输出与文件交互
前面的基础笔记里已经零散提到过 print、println、readline 这些最简单的输入输出操作,但如果要真正写脚本或小工具,还需要文件读写和路径处理。
概述
Julia 中与 IO 相关的几类操作包括:
- 控制台输入输出;
- 文本文件和二进制文件;
open do风格的资源管理;- 常见的路径、目录和文件系统操作。
整体上,Julia 的 IO 风格更接近“显式流对象 + open do 管理资源”,而不是 MATLAB 那种偏工作区工具式接口。
控制台输入输出
print 和 println
最常用的输出函数仍然是:
1 | print("hello") |
区别很简单:
print不自动换行;println自动在末尾补一个换行。
也可以连续输出多个对象:
1 | println("x = ", 1, ", y = ", 2) |
字符串插值
更顺手的写法通常是字符串插值:
1 | x = 3 |
这种需求在 Python 中通过 f-string 实现,在 Julia 则使用 $() 语法。
show
和 print / println 相比,show 更偏向“给程序员看的表示形式”。
例如
1 | x = [1, 2, 3] |
在 REPL 中,很多对象默认显示效果本质上也和 show 有关。
格式化输出 Printf
如果需要更接近 C 风格的格式化,可以使用标准库 Printf 提供的宏:
1 | using Printf |
也可以生成格式化字符串:
1 | s = ("x = %.2f", 3.14159) |
对于简单输出,字符串插值已经足够;真正需要宽度、精度控制时,再考虑 Printf。
日志输出
简单脚本里直接 println 往往就够了,但只要代码稍微复杂一点,日志接口就比随手打印更合适。
Julia 标准库里的日志功能主要来自 Logging,不过很多常用宏在日常环境里可以直接使用。
常见日志宏
最常用的几个宏包括:
@debug@info@warn@error
例如:
1 | "starting" |
输出里一般会带上:
- 日志级别;
- 消息内容;
- 触发位置。
@debug
@debug 比较特殊,默认往往不会直接显示出来:
1 | "x = 1" |
这使得它很适合放一些调试时才需要的信息,而不会污染平时输出。
带键值对的日志
日志宏支持顺手附带上下文信息:
1 | epoch = 3 |
这比手工拼字符串更自然,也更利于后续统一处理。
自定义日志后端
更完整的日志控制可以通过 Logging 标准库和自定义 logger 完成,例如切换日志级别、重定向日志输出位置等。
这部分已经超出基础笔记的范围,这里只需要知道 Julia 自带了一套正式日志接口,而不是只能靠 println。
控制台输入
readline
从标准输入读取一整行:
1 | s = readline() |
返回值是字符串。
如果需要数字,通常再配合 parse:
1 | s = readline() |
read
也可以直接从输入流读取数据:
1 | data = read(stdin, String) |
这会把标准输入剩余部分全部读成一个字符串。
文件的基本读写
一次性读取整个文本文件
最简单的方式:
1 | text = read("data.txt", String) |
这适合文本文件不大、希望一次性读入的情况。
一次性写入文本文件
1 | write("out.txt", "hello\nworld\n") |
需要注意:
write返回写入的字节数;- 这里写入的是原始字节内容。
如果要反复追加文本,更常见的写法是用 open。
open 与 do 语法
基本形式
文件最常见的打开方式如下:
1 | open("data.txt", "r") do io |
这里 io 就是文件流对象。
do 块结束后,文件会自动关闭。这和 Python 的 with open(...) as f: 很接近。
为什么推荐 open do
原因很简单:
- 不容易忘记关闭文件;
- 出异常时也更安全;
- 写法很适合把“打开资源 -> 做事 -> 自动释放资源”合在一起。
不带 do 的形式
当然也可以手动打开和关闭:
1 | io = open("data.txt", "r") |
但除非有特殊原因,否则一般不如 open do 清晰。
常见文件打开模式
open 常见模式包括:
"r":只读"w":写入,若文件已存在则覆盖"a":追加写入"r+":读写
例如追加写入:
1 | open("log.txt", "a") do io |
这里 println(io, ...) 表示把内容写入文件流,而不是控制台。
按行读写
eachline
逐行读取文件最方便的方式之一:
1 | open("data.txt", "r") do io |
如果只需要文件路径,也可以直接:
1 | for line in eachline("data.txt") |
readlines
如果希望一次性读成字符串数组:
1 | lines = readlines("data.txt") |
这会把每一行作为一个字符串元素放进数组中。
写多行文本
1 | open("out.txt", "w") do io |
二进制读写
Julia 的 IO 不只适合文本,也很适合二进制数据。
写二进制
1 | open("data.bin", "w") do io |
读二进制
1 | open("data.bin", "r") do io |
读取全部字节
1 | bytes = read("data.bin") |
返回值通常是 Vector{UInt8}。
文件系统操作
判断文件和目录
1 | isfile("data.txt") |
创建目录
1 | mkdir("tmpdir") |
区别:
mkdir只创建一级目录;mkpath会递归创建缺失的父目录。
删除文件和目录
1 | rm("data.txt") |
删除目录时通常要加 recursive=true。
列出目录内容
1 | readdir(".") |
join=true 时会返回拼接后的路径,而不是单独文件名。
复制、移动
1 | cp("a.txt", "b.txt") |
路径处理
pwd 和 cd
1 | pwd() |
脚本里仍然更推荐显式使用 @__DIR__ 来定位当前脚本目录:
1 | cd() |
joinpath
拼接路径时不要手写分隔符,直接使用:
1 | path = joinpath("data", "images", "a.png") |
这能避免不同操作系统路径分隔符差异的问题。
拆路径
常见函数包括:
1 | dirname(path) |
例如:
1 | splitext("demo.txt") # ("demo", ".txt") |
临时文件与内存 IO
临时文件
可以创建临时文件:
1 | (path, io) = mktemp() |
或者临时目录:
1 | dir = mktempdir() |
这在测试和中间数据处理时很方便。
IOBuffer
有时不想真的操作磁盘文件,只想在内存里模拟一个 IO 对象,可以使用 IOBuffer:
1 | buf = IOBuffer() |
这在测试、字符串构造和某些接口适配里非常有用。
序列化与结构化数据
严格来说,这部分已经不只是基础文件交互了,但值得顺手提一下。
内置序列化
Julia 自带 Serialization 标准库:
1 | using Serialization |
这适合 Julia 内部对象的快速保存和恢复,但不适合拿来做通用交换格式。
结构化文本
如果要处理 JSON、CSV、TOML 这些结构化格式,通常会用对应的包,例如:
JSON.jlCSV.jlTOML(标准库)
这些已经超出最基础的文件 IO,但在实际项目中非常常见。
小结
基础 IO 其实没有多少花样,记住几组最常用的接口就够了:文本文件用 read / write,涉及资源管理时优先写成 open(...) do io ... end,逐行处理用 eachline,路径处理用 joinpath 和 @__DIR__,剩下的文件系统操作按需要查标准库接口即可。
