1 | 文件 I/O
普通文件,有时也称为平面文件(flat file),是最简单的持久化形式。将文件内容读入内存,再将内存中的内容写入文件,标准库os
提供了许多系统函数,Python文件操作像 Unix
1-1 | 基本操作
-
open(filename, mode)
打开文件,目标文件不存在,则创建文件
mode
的第一个字符指定了操作r
表示读取。w
表示写入。如果文件不存在,则创建文件;如果同名文件存在,就覆盖该文件。x
表示仅在指定文件不存在时写入。a
表示如果文件存在,则在文件末尾追加写入
mode
的第二个字符指定了文件类型。t
(或忽略)表示文本文件b
表示二进制文件
new_file = open('new_text.txt','wt')
处理文件后,必须用
new_file.close()
关闭文件,确保写入操作完成并释放内存 -
print(text, file=file_obj)
可用于向文件中写入文本,传入一个
open()
打开的文件对象,由于参数text
会转成字符串,因此无法写入二进制模式的文件⚠️print('A new line', file=new_file)
-
write(fd, str, /)
写入文件以
wb
模式打开文件,则写入二进制数据>>> poem = "This is a fake poem...." >>> f = open('file_name.txt', 'wb') >>> f.write(poem) 23 # 返回写入文本的长度 >>> f.close()
💡 若源字符串较长,则分段写入
with open('test.txt', 'wt') as f: offset, chunk = 0, 100 while offset < len(poem): f.write(poem[offset:offset+chunk]) offset = offset + chunk
-
read()
readline()
readlines()
读取文本💡
read()
一次性读取所有内容,并消耗等量的内存,可以通过read(n)
读取指定数量的字符file.readline() # 读取一行,返回字符串 file.readlines() # 读取所有行,返回 list[str]
-
tell()
seek()
读取位置无论读写,Python 会追踪文件位置,
tell()
查看当前距文件起始的字节偏移seek(offset, from_what)
可跳转到指定的文件字节偏移位置
💡二者主要用于二进制文件。也可用于文本文件,但除非该文件采用的是 ASCII 编码(一个字符对应一字节),否则计算偏移值很麻烦。具体取决于文本编码,UTF-8 对每个字符使用的字节数不尽相同
-
自动关闭
with
Python 的上下文管理器
context manager
会清理一些资源,完成操作后应关闭文件with open('filename','wt') as file
1-2 | 文件打开后没有 close 会怎样❓
如果在函数内打开了文件,但没有关闭,那么当函数结束时,该文件会被自动关闭。如果是在长时间运行的函数或主程序部分打开的文件,则应该主动关闭,强制完成未执行的写操作
1-3 | 内存映射
读写文件的另一种方法是使用 mmap
模块进行内存映射。使文件内容类似内存中的 bytearray
1-4 | 显示声明编码
Windows 系统会出现的问题,open
() 时务必指明字符集
>>> open('cafe.txt', 'w', encoding='utf_8').write('café')
4
>>> open('cafe.txt').read()
'caf茅'
>>> open('cafe.txt', encoding='utf-8').read()
'café'
Python 处理文件时如何获取默认字符集?
import locale
>>> locale.getpreferredencoding()
'cp936'
>>> sys.getdefaultencoding()
'utf-8'
2 | 文件操作
2-1 | copy()
shutil.move()
函数会先复制文件,然后删除源文件
import shutil
>>> shutil.copy('new_text.txt','new.txt') # 复制文件
'new.txt'
>>> shutil.move('...','...') # 相当于剪切
2-2 | remove()
os.remove('c.txt') #删除文件
2-3 | exists()
检查文件或目录是否存在,传入相对或绝对路径
import os
>>> os.path.exists('..') # 表示上层目录
True
>>> os.path.exists('filename') # check a file
True
2-4 | isfile() isdir() isabs()
检查一个名称是文件、目录还是绝对路径
>>> name = 'new_text.txt'
>>> os.path.isfile(name)
True
# 单点号`.`表示当前目录,双点号`..`表示父目录
>>> os.path.isdir('.')
True
# 仅检查,非真实路径亦可
>>> os.path.isabs('D:\Alita')
True
2-5 | rename()
剪切文件
os.rename('new.txt','n.txt')
os.rename('redis1.md', 'static/redis1.md') # also can use to move file
2-6 | link() symlink() islink()
在Unix中,文件只存在一个位置,但可有多个名称,即链接
对于低层的硬链接而言,很难找出特定文件的所有名称
符号链接则是一种以独立文件的形式存储文件新名称的方法,可以同时获得原始名称和新名称
>>> os.link('n.txt','b.txt') # 创建一个硬链接
>>> os.path.isfile('b.txt')
True
>>> os.path.islink('b.txt')
False
>>> os.symlink('n.txt','a.txt') # 创建符号链接
>>> os.path.islink('a.txt')
True
2-7 | chmod()
该方法可修改文件权限,入参是文件名和一个八进制的值
>>> os.chmod('n.txt', 0o400)
# stat 模块中有常量,可避免八进制字符
>>> import stat
>>> os.chmod('n.txt', stat.S_IRUSR)
2-8 | chown()
可指定用户ID和组ID来修改文件所有者或所有组, 类 Unix 系统特有功能
>>> uid, gid = 1, 2
>>> os.chown('n', uid, gid)
3 | 目录操作
3-1 | 基础操作
os.getcwd() # 获取当前目录
os.mkdir('folder') # 创建目录
os.rmdir('folder') # 删除目录
os.listdir('Dance') # 列出目录内容
['settings.py', 'urls.py', 'wsgi.py', '__init__.py', '__pycache__']
os.chdir('foldername') # 切换目录
3-2 | 列出匹配文件
glob()
可根据 Unix shell 的通配符规则列出匹配的文件名或目录名。规则如下:
\*
匹配任意多个字符(对应的正则表达式为.*
);?
匹配单个字符;[abc]
匹配a
或b
或c
;[!abc]
匹配除a
、b
、c
之外的任意字符。
import glob
>>> glob.glob('a*') # 查找以a开头的文件或路径
['admin.py', 'apps.py']
>>> glob.glob('????????') # 查找文件或目录名为8个字符的
['admin.py', 'tests.py', 'views.py']
>>> glob.glob('a??????y') # 查找首末结尾为a和y 且中间有6个字符的文件或路径
['admin.py']
>>> glob.glob('[af]*y') #查找所有以af开头,以y为结尾的文件和目录
['admin.py', 'apps.py', 'fibo.py']
4 | 路径名
Unix、Mac、Web URL 使用斜线 /
作为路径分隔符,Windows 则使用反斜线 \
反斜线在 Python 中用作转义字符,所以要么使用连续两个反斜线 \\
,要么使用 Python 的原始字符串 r""
4-1 | abspath()
相对路径 —扩展—>绝对路径
>>> os.path.abspath('n.txt')
'D:\\n.txt'
4-2 | realpath()
获取绝对路径,符号链接所指向的文件
>>> os.path.realpath('a.txt')
'D:\\a.txt'
4-3 | os.path.join()
以当前操作系统的路径分隔符拼接路径,直接拼接字符串导致写死的分隔符在不同系统间不通用
p = os.path.join("dir1", "filename")
>>> p # linux
'dir1/filename'
>>> p # win
'dir1\\filename'
4-4 | pathlib
🆕 Python 3.4 新增,用于代替 os.path
模块
区别:将路径视为对象 ,不再是普通的字符串,通过 /
拼接
from pathlib import Path
new_path = Path('aaa') / Path('words.txt')
>>> new_path
WindowsPath('aaa/words.txt')
💡获取路径信息更加方便
>>> new_path.name
'words.txt'
>>> new_path.suffix
'.txt'
>>> new_path.stem
'words'
>>> new_path.parent
WindowsPath('aaa')
5 | BytesIO、StringIO
需求:要处理内存中的数据,但调用的函数只接受文件(或者是相反的情况)?
io.BytesIO
和 io.StringIO
,前者用于二进制数据bytes
,后者用于文本数据str
使用场景:数据格式转换
from io import BytesIO
from PIL import Image
def data_to_img(data):
"""Return PIL Image object, with data from in-memory <data>"""
fp = BytesIO(data)
return Image.open(fp) # 读取内存
def img_to_data(img, fmt=None):
"""Return image data from PIL Image <img>, in <fmt> format"""
fp = BytesIO()
if not fmt:
fmt = img.format # 保存原始格式
img.save(fp, fmt) # 写入内存
return fp.getvalue()
💡getvalue()
可以返回 BytesIO
对象的所有字节
END