文件操作:文件的打开关闭和读写
上下文管理 with
路径操作os.path模块、pathlib模块
os模块

文件操作


打开文件

  • open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
  • 基本操作
f = open("test") # file对象
# windows <_io.TextIOWrapper name='test' mode='r' encoding='cp936'>
# linux <_io.TextIOWrapper name='test' mode='r' encoding='UTF-8'>
print(f.read()) # 读取文件
f.close() # 关闭文件

windows 中使用 codepage 代码页,可以认为每一个代码页就是一张编码表。cp936 等同于 GBK。

open 的参数

  • mode 模式
  1. r 模式
    • 默认打开模式
    • 只读打开文件,如果使用 write 方法,会抛异常。
    • 如果文件不存在,抛出 FileNotFoundError 异常
  2. w 模式
    • 表示只写方式打开,如果读取则抛出异常
    • 如果文件不存在,则直接创建文件
    • 如果文件存在,则清空文件内容,即会用空文件覆盖之前的文件
  3. x 模式
    • 文件不存在,创建文件,并只写方式打开
    • 文件存在,抛出 FileExistsError 异常
    • 要求文件不存在
  4. a 模式
    • 文件存在,只写打开,追加内容
    • 文件不存在,则创建后,只写打开,追加内容
  5. +模式
    • 为 r、w、a、x 提供缺失的读或写功能,但是,获取文件对象依旧按照 r、w、a、x 自己的特征
    • +模式不能单独使用,可以认为它是为前面的模式字符做增强功能的

总结:

  • r 是只读,wxa 都是只写
  • wxa 模式都可以产生新文件
  • w 不管文件是否存在,都会生成全新的内容文件
  • a 不管文件是否存在,都能在打开的文件尾部追加
  • x 必须要求文件不存在,自己造一个新的文件
  1. 文本模式 t

    • 字符流,将文件的字节按照某种字符编码理解,按照字符操作
    • open 的默认缺省模式就是 rt
  2. 二进制模式 b

    • 字节流,将文件按照字节理解,与字符编码无关
    • 二进制模式操作时,字节操作使用 bytes 类型
  3. 其他参数
    待补充

文件指针

  • 文件指针,指向当前字节位置
  • r 模式,指针起始位置为 0,a 模式指针起始位置为 EOF

tell

  • 显示指针当前位置

seek

  • seek(offset[,whence]) 移动文件指针位置
  • offset 为偏移字节数
  • 可以超界,当向前 seek 时,不能超界,否则抛异常
  • whence 表示从哪里开始偏移
    • 0 缺省值,表示相对于开头开始偏移
    • 1 表示相对于当前位置偏移
    • 2 表示相对于 EOF 位置偏移
  • 文本模式下
    • whence 是 0 时,offset 只能是正整数,seek(n) 即 seek(n,0),n 可超右边界
    • whence 是 1 时,offset 只能是 0 即只能使用 seek(0,1) 文件指针不动
    • whence 是 2 时,offset 只能是 0,即 seek(0,2) 把文件指针移到末尾
  • 二进制模式下
  • whence 是 0 时,offset 只能是正整数
  • whence 是 1 时,offset 可正可负
  • whence 是 2 时,offset 可正可负

文件读写

read

  • read(size=-1)
  • size 表示读取的多少个字符(文本模式)或者字节(二进制模式)
  • size 为负数或者 None 表示从当前文件指针位置读取到 EOF
# 在工作目录创建文件并写入123456789
f = open('test','rt')
print(f.tell()) # 文件开头
print(f.read(1)) # 读一个字符
print(f.read()) # 从当前文件指针读到末尾
print(f.tell()) # 当前文件指针在末尾
print(f.seek(0)) # 移动文件指针到开头
print(f.read(3)) # 读三个字符
print(f.tell()) 
print(f.seek(0,2)) # 移动文件指针到末尾
print(f.read()) # 空
f.close()
-----------------------
0
1
23456789
9
0
123
3
9

readline

  • readline(size=-1), 一行行读取文件内容
  • size 设置一次能读取行内几个字符或者字节

readlines

  • readlines(hint=-1), 读取所有行的列表
  • 指定 hint 则返回指定的行数
f = open('test')
print(f.readline(),end='')
print(f.readlines())
-------------------------
a
['b\n', 'c\n', 'd']

# 推荐做法
f = open('test') # f就是可迭代对象
for line in f:
    print(line,end='')
f.close()
----------------------
a
b
c
d

# 惰性方式
def rf():
   yield from open('test') 
for i in rf():
    print(i, end='')

write

  • write(s), 把字符串 s 写入到文件中
  • 并返回字符的个数

writelines

  • writelines(lines), 将字符串列表写入文件
f = open('test','w+')
lines = ['a','b\n','c','d'] # 这种方式要想换行,要自己加
f.writelines(lines)
f.close()
print(open('test').read())
---------------------------
ab
cd

f = open('test','w+')
lines = ['a','b','c','d']
f.write('\n'.join(lines))
f.seek(0)
print(f.read())
f.close()
--------------------------
a
b
c
d

关闭文件

close

  • flush 并关闭文件对象
  • 文件已经关闭,再次关闭无影响,可多次关闭

其他

  1. seekable() 是否可以 seek
  2. readable() 是否可读
  3. writeable() 是否可写
  4. closed 是否已经关闭 f.closed
    一种特殊的语法,交给解释器去释放文件对象

上下文管理with

  • 使用 with...as 关键字
  • 上下文管理的语句块并不会开启新的作用域,函数 / 类 / 模块中有作用域的概念
  • with 语句块执行完的时候,会自动关闭文件对象
  • 打开文件由 open 函数完成,跟 with 无关
with open('test') as f: # 上下文管理:进入时f = open('test').__enter__()——> 离开时关闭文件对象
    print(f.read(),f.closed)
print('~'*20)
print(f.closed)
---------------------------
False
~~~~~~~~~~~~~~~~~
True
  • 无论 with 语句块中是否中有异常,都能关闭文件对象
# 可以在jupyter中运行
with open('test') as f:
    print(f.read(),f.closed)
    1/0
    print('*********************')
print(f.closed)

# print(f.closed)
--------------------------------
 False
Traceback (most recent call last):
    1/0
ZeroDivisionError: division by zero
# True

路径操作


os.path模块

py3.4之前使用

  1. 路径拼接os.path.join
  2. 路径存在os.path.exists
  3. 路径分割os.path.split
  4. 路径os.path.dirname和路基os.path.basename
  5. 标准路径os.path.realpath:返回指定文件的标准路径,真实路径,如果为软链接,返回指向的路径
  6. 绝对路径os.path.abspath:返回一个目录的绝对路径
import os
p = os.path.join('/usr', 'local', 'bin/python3')  # 拼接路径
print(type(p), p)  # str
print(os.path.split(p))  # ('/usr/local/bin', 'python3')
print(os.path.exists(p))  # 是否存在
print(os.path.realpath(p))  # 真实路径
print(os.path.abspath(p))  # 绝对路径,注意于realpath的区别
print(os.path.basename(p))  # 路基,最后一级
print(os.path.dirname(p))  # 路径,不包含最后一级

THIS_FILE_PATH = os.path.dirname(os.path.realpath(__file__)) # 当前文件路径
print(THIS_FILE_PATH)
  1. 判断文件或目录
    • os.path.isfile
    • os.path.isdir

os模块

  1. 获取操作系统平台
    • os.name:windows 返回nt,linux返回posix
    • os.uname:*nix平台支持
    • sys.platform:win返回win32,linux返回linux
  2. 常用
    • os.getcwd():获取当前工作目录
    • os.listdir():返回指定目录下的所有文件和目录名
    • os.walk():目录遍历器,在目录树中游走输出在目录中的文件名,参看os.walk
# 找出文件夹下所有的py文件
import os

def find_py_files(folder):
    py_files = []
    for root, dirs, files in os.walk(folder):
        for file in files:
            if file.endswith('.py'):
                py_files.append(os.path.join(root, file))
    return py_files

# Example usage
folder = '/path/to/folder'
py_files = find_py_files(folder)
print(py_files)
  • os.remove():删除一个文件
  • os.system():运行shell命令
  • os.mkdir():生成单级目录
  • os.makedirs():生成多层递归目录
  • os.rmdir()os.removedirs():删除目录
  • os.rename():重命名文件或目录
  1. 其他
    • os.stat(path, *, dir_fd=None, follow_symlinks=True)
    • os.chmod(path, mode, *, dir_fd=None, follow_symlinks=True)
    • os.chown(path, uid, gid)

pathlib模块

>=py3.4建议使用,提供Path对象来操作,包含目录和文件

初始化

from pathlib import Path

p = Path()  # 或 Path('.') 或 Path('') 表示当前目录
p1 = Path('a', 'b', 'c/d')  # 当前目录下的a/b/c/d
p2 = Path('/etc')  # 根目录下的etc目录

路径拼接和分解

  1. /joinpath
    • Path对象/Path对象、Path对象/字符串 或者 字符串/Path对象
    • 不能字符串 / 字符串
    • joinpath:joinpath(*other) 连接多个字符串到Path对象中
  from pathlib import Path

  p = Path()
  p = p / 'a'
  p1 = 'b' / p
  p2 = Path('c')
  p3 = p2 / p1
  # p4 = 'e' / 'f' / p3  # TypeError: unsupported operand type(s) for /: 'str' and 'str'
  p5 = p3 / 'e' / 'f'
  print(p5)
  print(p5.parts)
  p6 = p5.joinpath('etc', 'init.d', Path('httpd'))
  print(p6)

注意:p~p6这里并不是字符串,而是Path类实例,可查看type(p6)

  1. parts属性
    • 返回元组
    • 返回的是路径中的每一个部分,见上例

绝对路径

  • resolve():当前Path对象的绝对路径,如果是软链接则直接被解析
  • absolute():获取绝对路径

获取路径

  1. str 获取路径字符串
  2. bytes获取路径字符串的bytes
p = Path("/usr/local/bin/python3")
print(type(p))
print(str(p))
print(bytes(p))
print(p.resolve())
print(p.absolute())
------------------
<class 'pathlib.PosixPath'>
/usr/local/bin/python3
b'/usr/local/bin/python3'
/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/bin/python3.8
/usr/local/bin/python3

父目录

  1. parent 目录的逻辑父目录
  2. parents 父目录序列
    • 返回可迭代对象
    • 索引0是直接的父,即parent
p = Path('/a/b/c/d')
print(p.parent.parent)
print(p.parent)
print(p.parents[0])
for x in p.parents:
      print(x)
# print(p.parents[-1]) # raise IndexError(idx)
print(p.parents[len(p.parents)-1])

全局方法

  • cwd() 返回当前工作目录
  • home() 返回当前家目录
p  = Path('a/b')
print(Path.cwd())
print(p.home())

目录组成部分

name、stem、suffix、suffixes、with_suffix(suffix)、with_name(name)

  • name 目录的最后一个部分
  • suffix 目录中最后一个部分的扩展名
  • stem 目录最后一个部分,没有后缀
  • name = stem + suffix
  • suffixes 返回多个扩展名列表
  • with_suffix(suffix) 有扩展名则替换,无则补充扩展名
  • with_name(name) 替换目录最后一个部分并返回一个新的路径
p = Path('/monkey/mysqlinstall/mysql.tar.gz')
print(p.name) # 目录的最后一部分mysql.tar.gz
print(p.suffix) # 目录最后一部分的扩展名.gz
print(p.suffixes) # 多个扩展名列表 ['.tar', '.gz']
print(p.stem) # 目录最后一部分,没有后缀mysql.tar
print(p.with_name('mysql-5.tgz')) # 替换最后一部分,返回新的路径/monkey/mysqlinstall/mysql-5.tgz
print(p.with_suffix('.png')) # 有扩展名则替换/monkey/mysqlinstall/mysql.tar.png
p = Path('README')
print(p.with_suffix('.txt')) # 无则补充扩展名,README.txt

判断方法

  • exists() 目录或文件是否存在
  • is_dir() 是否是目录,目录存在返回True
  • is_file() 是否是普通文件,文件存在返回True
  • is_symlink() 是否是软链接
  • is_socket() 是否是socket文件
  • is_block_device() 是否是块设备
  • is_char_device() 是否是字符设备
  • is_absolute() 是否是绝对路径
p = Path("/usr/local/bin/python3")
print(p.exists())
print(p.is_file())
print(p.is_dir())
print(p.is_symlink())

迭代目录

  • iterdir():迭代当前目录,不递归
    如下目录结构:
1
├── 1.py
├── 2
│   └── test.py
├── 2.py
└── 3
p = Path('1')
print(p.iterdir(), type(p.iterdir()))
for i in p.iterdir():
    print(i)

操作

  • rmdir() : 删除空目录,当前Path未提供目录判空方法,可用iterdir来判断
  • touch(mode=0o666, exist_ok=True) :创建一个文件
  • as_uri() :将路径返回成URI,例如'file:///etc/passwd'
  • mkdir(mode=0o777, parents=False, exist_ok=False)

parents,是否创建父目录,为True等同于shell命令mkdir -p。False时,父目录不存在,则抛出FileNotFoundError
exist_ok参数,在3.5版本加入。False时,路径存在,抛出FileExistsError;True时,FileExistsError被忽略

from pathlib import Path

p = Path('a/b/c/')
# p.mkdir() # No such file or directory: 'a/b/c'
p.mkdir(parents=True)
print(p.exists())
# p.mkdir(parents=True)  # [Errno 17] File exists: 'a/b/c'
p.mkdir(parents=True, exist_ok=True)
p.rmdir()
p = Path('a/b')
p /= 'readme.txt' # 或 p.with_name('readme.txt').touch()
print(p.exists())
p.touch()
print(p.exists())
# p.parent.rmdir()  # Directory not empty: 'a/b'
# 遍历目录,判断是否为文件类型,或者目录,如果是目录并判断是否为空
from pathlib import Path
'''
a
├── 1.py
├── b
│   └── readme.txt
└── c

'''
p = Path('a')
for i in p.iterdir():
    if i.is_file():
        print(f'{i} is file')

    elif i.is_dir():
        flag = False
        for _ in i.iterdir():
            flag = True
            break
        print(i, 'not empty' if flag else 'empty')
    else:
        print('other file')
# 在当前目录下创建目录d/e/1.txt
from pathlib import Path

p = Path('d/e/1.txt')
if not p.parent.exists():
    p.parent.mkdir(parents=True)
p.touch(exist_ok=True)

通配符和匹配

  • glob(pattern):通配给定的模式
  • rglob(pattern):通配给定的模式,递归目录
  • glob和rglob都返回一个生成器
  • ?一个字符,*任意个字符,[a-z]a-z中一个字符
  • match(pattern):匹配模式,成功则返回True
from pathlib import Path
'''
a
├── b
│   ├── b.py
│   └── b.zip
├── c
│   └── test.py
└── test1.py
'''
p = Path('a')
print(list(p.glob('test*')))
print(list(p.glob('**/*.py')))  # 递归所有目录,等同rglob
print(list(p.glob('**/*')))
g = p.rglob('*.py')  # 生成器,递归
print(next(g))
print(list(p.rglob('*.???')))
print(list(p.rglob('[a-z]*.??')))
--------------mac上执行结果------------------
[PosixPath('a/test1.py')]
[PosixPath('a/test1.py'), PosixPath('a/c/test.py'), PosixPath('a/b/b.py')]
[PosixPath('a/test1.py'), PosixPath('a/c'), PosixPath('a/b'), PosixPath('a/c/test.py'), PosixPath('a/b/b.zip'), PosixPath('a/b/b.py')]
a/test1.py
[PosixPath('a/b/b.zip')]
[PosixPath('a/test1.py'), PosixPath('a/c/test.py'), PosixPath('a/b/b.py')]

文件信息

  • stat() 相当于stat命令
  • lstat() 同stat(),但如果是符号链接,则显示符号链接本身的文件信息

文件操作

  • Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
  • Path.read_bytes()
  • Path.read_text(encoding=None, errors=None)
  • Path.write_bytes(data)
  • Path.write_text(data, encoding=None, errors=None)