输入输出
f-string 负责格式化,with open 负责读写文件,json 模块负责结构化数据,掌握这三样就够用了。
字符串格式化
f-string 是首选的格式化方式。花括号里可以写任意表达式,冒号后面是格式说明。
f-string
name, pi = "Pi", 3.14159
print(f"{name} ≈ {pi:.2f}") # => Pi ≈ 3.14
print(f"{1234567:,}") # => 1,234,567
print(f"[{'左':<4}|{42:>4}]") # => [左 | 42]
print(f"{pi=}") # => pi=3.14159var name = "Pi";
var pi = 3.14159;
System.out.println(String.format(
"%s ≈ %.2f", name, pi)); // => Pi ≈ 3.14
System.out.println(String.format(
"%,d", 1234567)); // => 1,234,567
System.out.println(String.format(
"[%-4s|%4d]", "左", 42)); // => [左 | 42]val name = "Pi"
val pi = 3.14159
println("$name ≈ ${"%.2f".format(pi)}") // => Pi ≈ 3.14
println("%,d".format(1234567)) // => 1,234,567
println("[%-4s|%4d]".format("左", 42)) // => [左 | 42]| 对比项 | Python | Java |
|---|---|---|
| 插值 | f"{x}" | 没有内置语法 |
| 数字格式 | {pi:.2f},写在变量旁边 | %.2f,写在占位符里 |
| 调试输出 | f"{pi=}" | 没有 |
| 格式符含义 | 相同.2f 表示两位小数 | |
| 对比项 | Python | Kotlin |
|---|---|---|
| 插值 | f"{x}" | "$x" |
| 数字格式 | {pi:.2f},写在变量旁边 | 插值配合 format |
| 调试输出 | f"{pi=}" | 没有 |
| 格式符含义 | 相同.2f 表示两位小数 | |
{pi=} 同时输出表达式和值,调试时很好用。
| Python | Java | Kotlin | 说明 |
|---|---|---|---|
f"{x:.2f}" | "%.2f" | "%.2f".format(x) | 保留两位小数 |
f"{x:>8}" | "%8s" | "%8s".format(x) | 右对齐 |
f"{x:<8}" | "%-8s" | "%-8s".format(x) | 左对齐 |
f"{x:08.3f}" | "%08.3f" | "%08.3f".format(x) | 用 0 补齐 |
f"{x:,}" | "%,d" | "%,d".format(x) | 千分位 |
f"{x:.1%}" | 无 | 无 | 百分比,0.25 显示为 25.0% |
f"{x:x}" | "%x" | "%x".format(x) | 十六进制 |
f"{x!r}" | 无 | 无 | 用 repr() 而不是 str() |
str() 生成给人看的文本,repr() 生成给开发者看的文本,通常带引号或类型信息。print 用前者,交互模式显示后者。
老代码里的格式化写法选读
你在老项目里还会看到两种写法。"{} 和 {}".format(a, b) 是 f-string 出现之前的主流方式。"%s 和 %d" % (a, b) 是更早的 C 风格写法,今天主要用在 logging 里。字符串的 rjust()、ljust()、zfill() 方法也能做简单对齐。新代码统一用 f-string。
读写文件
open() 返回文件对象,配合 with 自动关闭。文本文件要明确写 encoding="utf-8",否则默认编码取决于操作系统。
读写文本文件
with open("a.txt", "w", encoding="utf-8") as f:
f.write("第一行\n第二行\n")
with open("a.txt", encoding="utf-8") as f:
for line in f:
print(line.rstrip())
with open("a.txt", encoding="utf-8") as f:
print(len(f.read())) # => 8var path = Path.of("a.txt");
Files.writeString(path, "第一行\n第二行\n");
for (var line : Files.readAllLines(path)) {
System.out.println(line);
}
var text = Files.readString(path);
System.out.println(text.length()); // => 8import java.io.File
val file = File("a.txt")
file.writeText("第一行\n第二行\n")
file.forEachLine { println(it) }
println(file.readText().length) // => 8| 对比项 | Python | Java |
|---|---|---|
| 写入 | f.write(text) | Files.writeString |
| 逐行读取 | for line in f | Files.readAllLines,大文件用 Files.lines |
| 读取全部 | f.read() | Files.readString |
| 默认编码 | 取决于操作系统 | UTF-8 |
| 行尾换行符 | 保留,用 rstrip() 去掉 | 自动去掉 |
| 对比项 | Python | Kotlin |
|---|---|---|
| 写入 | f.write(text) | file.writeText |
| 逐行读取 | for line in f | file.forEachLine |
| 读取全部 | f.read() | file.readText |
| 默认编码 | 取决于操作系统 | UTF-8 |
| 行尾换行符 | 保留,用 rstrip() 去掉 | 自动去掉 |
直接遍历文件对象就是逐行读取,不会把整个文件读进内存。
| Python | Java | Kotlin | 说明 |
|---|---|---|---|
open(p) | Files.newBufferedReader(p) | file.bufferedReader() | 默认只读文本模式 |
open(p, "w") | Files.newBufferedWriter(p) | file.bufferedWriter() | 覆盖写入 |
open(p, "a") | Files.writeString(p, s, APPEND) | file.appendText(s) | 追加写入 |
open(p, "x") | Files.writeString(p, s, CREATE_NEW) | 无 | 文件已存在时报错 |
open(p, "rb") | Files.newInputStream(p) | file.inputStream() | 二进制模式 |
f.read() | Files.readString(p) | file.readText() | 读取全部 |
f.readlines() | Files.readAllLines(p) | file.readLines() | 读成行列表 |
Path(p).read_text() | Files.readString(p) | file.readText() | 一行读完,不用 with |
文件对象的其他方法选读
f.readline() 读一行,读到末尾时返回空字符串。f.write() 返回写入的字符数。f.tell() 返回当前位置,f.seek(0) 跳回开头。模式 "r+" 同时支持读写。文本模式会把换行符统一转成 \n,处理图片这类数据时一定要用二进制模式。
JSON
json 模块在标准库里。JSON 对象直接映射成 dict,数组映射成 list,不需要先定义类。
JSON 序列化
import json
data = {"name": "Ann", "tags": ["a", "b"]}
text = json.dumps(data, ensure_ascii=False)
print(text) # => {"name": "Ann", "tags": ["a", "b"]}
back = json.loads(text)
print(back["tags"][0]) # => a
with open("d.json", "w") as f:
json.dump(data, f)// 使用第三方库 Jackson
var mapper = new ObjectMapper();
var data = Map.of("name", "Ann");
var text = mapper.writeValueAsString(data);
var back = mapper.readValue(text, Map.class);// 使用 kotlinx.serialization
@Serializable
data class User(val name: String)
val text = Json.encodeToString(User("Ann"))
val back = Json.decodeFromString<User>(text)| 对比项 | Python | Java |
|---|---|---|
| 支持方式 | 标准库 json | 第三方库 Jackson、Gson |
| 解析结果 | dict 和 list | 通常绑定到类 |
| 对象转字符串 | json.dumps | writeValueAsString |
| 字符串转对象 | json.loads | readValue |
| 对比项 | Python | Kotlin |
|---|---|---|
| 支持方式 | 标准库 json | 第三方库 kotlinx.serialization |
| 解析结果 | dict 和 list | 绑定到类 |
| 对象转字符串 | json.dumps | encodeToString |
| 字符串转对象 | json.loads | decodeFromString |
dumps 和 loads 处理字符串,多出来的 s 代表 string,dump 和 load 处理文件。需要校验时用第三方库 pydantic。
pickle选读
pickle 能序列化几乎任何 Python 对象,作用类似 Java 的 Serializable。它只能在 Python 之间使用,而且加载不可信的数据会执行任意代码。跨语言或跨系统交换数据时用 JSON。
字节和编码
Python 严格区分文本和字节。str 是文本,bytes 是字节序列,两者不能混用。读二进制文件、收发网络数据时拿到的都是 bytes。
编码和解码
text = "中文"
data = text.encode("utf-8")
print(data) # => b'\xe4\xb8\xad\xe6\x96\x87'
print(len(text), len(data)) # => 2 6
print(data.decode("utf-8")) # => 中文var text = "中文";
var data = text.getBytes(
StandardCharsets.UTF_8);
System.out.println(
text.length() + " " + data.length); // => 2 6
System.out.println(new String(
data, StandardCharsets.UTF_8)); // => 中文val text = "中文"
val data = text.toByteArray()
println("${text.length} ${data.size}") // => 2 6
println(data.decodeToString()) // => 中文| 对比项 | Python | Java |
|---|---|---|
| 字节类型 | bytes,不可变 | byte[],可变 |
| 字面量 | b"abc" | 没有 |
| 编码 | s.encode("utf-8") | s.getBytes(UTF_8) |
| 解码 | b.decode("utf-8") | new String(b, UTF_8) |
😀 的长度 | len 得到 1,按字符计 | length() 得到 2 |
| 对比项 | Python | Kotlin |
|---|---|---|
| 字节类型 | bytes,不可变 | ByteArray,可变 |
| 字面量 | b"abc" | 没有 |
| 编码 | s.encode("utf-8") | s.toByteArray() |
| 解码 | b.decode("utf-8") | b.decodeToString() |
😀 的长度 | len 得到 1,按字符计 | length 得到 2 |
str 和 bytes 相加会抛 TypeError。需要可变的字节序列时用 bytearray。
控制台输入
input("请输入:") 读取一行,总是返回字符串,要数字时写 int(input())。对应 Java 的 Scanner 和 Kotlin 的 readln()。
本章要点
- 格式化统一用 f-string,冒号后写格式说明,如
{x:.2f} - 文件用
with open(...)打开,文本文件明确写encoding="utf-8" - 遍历文件对象就是逐行读取,省内存
json.dumps和json.loads直接在dict和字符串之间转换str是文本,bytes是字节,用encode和decode互转
官方原文:输入与输出