with open("a.txt","r") as f: print(f) <_io.TextIOWrapper name='a.txt' mode='r' encoding='cp936'> 显示cp936,这个怎么来的 这个f对象,encoding底层是怎么取值的,这个TextIOWrapper 干啥用的
TextIOWrapper 是 “字节流 → 字符流” 的桥梁,位于 Python I/O 栈的最顶层:
磁盘/内核 → 操作系统文件描述符 → io.FileIO (原始字节流) ↓ io.BufferedReader/Writer (带缓冲) ↓ io.TextIOWrapper (编码/解码) ↓ 你的代码(拿到的 str)
str
;写文件时把 str
编码成字节。newline=
参数把 \r\n
、\r
、\n
统一成你想要的风格。readline()
、readlines()
、__iter__()
、write()
等,都是它实现的。cp936
来自 Windows 的默认 ANSI 代码页;
open()
在文本模式("r"
、"w"
等)时,返回的并不是裸文件描述符,而是一个 io.TextIOWrapper 对象。
__repr__
方法会把自己内部的 name / mode / encoding
打印出来,于是你就看到了:<_io.TextIOWrapper name='a.txt' mode='r' encoding='cp936'>
open()
里显式写了 encoding=
—— 那就用你给的。_io/textio.c
里,伪代码大致如下PyUnicode_GetDefaultEncoding()
最终落到:GetACP()
返回 936,于是 Python 把它翻译成字符串 "cp936"
。"utf-8"
cp936
cp1252
utf-8
import locale
print(locale.getpreferredencoding(False))
locale.getpreferredencoding(False)
是 Python 中用于获取系统首选编码的函数,其返回值通常与系统的区域设置(locale)相关
with open('a.txt', 'r', encoding='utf-8') as f: line = f.readline()
Python 解释器启动后,会依次创建 3 个对象,它们层层包裹,形成一条 “俄罗斯套娃” 式的流水线。
int fd
,Windows 下是 HANDLE
)打交道,只认识字节(bytes
)。b'hello\n'
这种原始字节串;写进去也必须喂 bytes
。FileIO.read()
→ C 函数 _io_FileIO_read
→ 系统调用 read(fd, buf, n)
→ 内核 VFS → 块设备驱动 → 磁盘控制器。read()
都会触发一次系统调用,性能差;因此 Python 几乎不会直接把它暴露给用户。peek()
、read1()
等更灵活的 API。bytes
,只是多了一段用户态内存缓冲。buffer
,read(10)
可能只需要从缓冲区里拷贝 10 字节,而不必到内核。open()
时,Python 会悄悄选择 buffering=-1
(默认 8 KB,8192字节)或你手动指定的 buffering
,然后自动套一层 BufferedReader/Writer
。BufferedReader.read() → 先看缓冲区够不够
↓ 不够 → FileIO.readinto(buffer) → 系统调用
↓ 够 → memcpy 给用户
str
),反向则编码。bytes
→ str
(已解码)str
→ bytes
(已编码).buffer
指向下一层的 Buffered 对象.encoding
保存你显式指定的,或 locale 推断出的编码名.decoder
是 Python 的 codecs.getincrementaldecoder(encoding)
实例TextIOWrapper.readline()
↓ 调 BufferedReader.read()
↓ 拿到 bytes
↓ decoder.decode(bytes) → str
↓ 按 newline 规则切出行尾
TextIOWrapper.write(text:str)
↓ encoder.encode(text) → bytes
↓ BufferedWriter.write(bytes)
Python 代码 │ │ "hello世界" ▼ ┌------------------------------┐ │ TextIOWrapper │ ← f │ encoding='utf-8' │ │ decode/encode + newline │ └-------------┬----------------┘ │ bytes ┌-------------┴----------------┐ │ BufferedReader/Writer │ ← f.buffer │ 8 KB 用户态缓冲 │ └-------------┬----------------┘ │ bytes ┌-------------┴----------------┐ │ FileIO (RawIOBase) │ ← f.buffer.raw │ fd=3 (a.txt) │ └-------------┬----------------┘ │ 系统调用 Linux 内核 │ ▼ 磁盘控制器
>>> f = open('a.txt', 'r', encoding='utf-8') >>> type(f) <class '_io.TextIOWrapper'> >>> type(f.buffer) <class '_io.BufferedReader'> >>> type(f.buffer.raw) <class '_io.FileIO'>
"rb"
二进制模式?buffering=0
会怎样?seek()
时字符偏移和字节偏移可能不一致?
磁盘/内核 → 操作系统文件描述符 → io.FileIO (原始字节流)
↓
io.BufferedReader/Writer (带缓冲)
↓
io.TextIOWrapper (编码/解码)
↓
你的代码(拿到的 str)