感觉写博客的时候,为了创建文件很麻烦,在创建文件的时候,需要给文件名特定的格式。而在写内容的时候也要加上特定的文件头。这实在让人头秃。
所以学习一下Python对文件的操作,然后在之后创建一个脚本。
参考文档
我们将通过学习以下几个部分来掌握这件事情。
- 获取文件属性
- 创建与删除与重命名目录和文件
- 文件名模式匹配
- 对文件的读写操作
我们有一个test
的文件,他下面的那个结构是
test
├── file1.py
├── file2.py
├── test1
│ └── file3.py
└── test2
└── file4.py
使用pathlib模块来处理
pathlib模块是提供来表示文件系统路径的类,在不同的操作系统中,我们有不同的子模块可以使用,但是如果以前从未使用过该模块,则Path
是最好的选择,他与操作系统无关。
本博客只记录了一些我认为我需要的内容,其余详细,查阅官方文档
初步使用
导入:
>>> from pathlib import Path
实例化一个对象:
>>> p = Path('test')
>>> p
PosixPath('test')
>>> type(p)
<class 'pathlib.PosixPath'>
>>> p.name
'test'
几个重要的属性
-
Path.parts
一个元组,路径的多个组件
>>> p = Path('test') >>> p.parts ('test',) >>> p = Path('home/smith/Desktop/test') >>> p.parts ('home', 'smith', 'Desktop', 'test')
-
Path.parents
提供对路径逻辑祖先的访问的不可变序列,我们将得到包含当前路径的所有祖先
>>> p = Path('home/smith/Desktop/test') >>> p_parents <PosixPath.parents> >>> p_parents = p.parents >>> p_parents[0] PosixPath('home/smith/Desktop') >>> p_parents[1] PosixPath('home/smith') >>> p_parents[2] PosixPath('home') >>> p_parents[3] PosixPath('.')
-
Path.parent
此路径的逻辑父路径
>>> p.parent PosixPath('home/smith/Desktop')
-
Path.name
一个表示最后路径组建的字符串,排除了驱动器和根目录
>>> p.name 'test' >>> p1 = Path('/') >>> p1.name ''
-
Path.suffix
最后一个组件的文件扩展名,如果没有,返回空字符串
>>> p = Path('test.py') >>> p.suffix '.py'
-
Path.stem
最后一个组件的文件名,去掉扩展名
最后一个路径组件,去除后缀
获取当前目录和当前用户家的目录
本过程我们需要使用Path.cwd()
方法
-
Path.cwd()
返回一个新的表示当前目录的路径对象
-
Path.home()
返回一个当前用户家目录的新路径对象
我们直接使用Path.cwd()
就可以获得当前路径的路径对象,根据不同的操作系统,会返回不同的对象
>>> Path.cwd()
PosixPath('/home/smith/Desktop')
>>> type(Path.cwd())
<class 'pathlib.PosixPath'>
Path.home()
与Path.cwd()
使用是一致的
>>> Path.home()
PosixPath('/home/smith')
获得文件属性
-
Path.iterdir()
当我们实例化的对象是一个目录时,返回一个包含路径下所有对象的生成器
-
Path.is_dir():
如果路径指向的是一个目录,则返回
True
,否则,返回False
-
Path.is_file():
如果路径指向的是一个正常的文件,则返回
True
, 否则返回False
-
Path.exists():
如果路径指向的是一个已存在的文件或目录,则返回
True
,否则返回False
-
Path.stat():
返回此路径的信息。结果在每次调用此方法时都重新产生。
获取目录下列表
我们使用pathlib.Path()
来返回一个poxispath
对象,然后我们就可以调用.iterdir()
来返回一个生成器,他包含了给定的路径下的所有的目录和文件。
>>> p
PosixPath('test')
>>> p.iterdir()
<generator object Path.iterdir at 0x7f1ffa47bf48>
>>> type(p.iterdir())
<class 'generator'>
>>> for child in p.iterdir(): child
...
PosixPath('test/file2.py')
PosixPath('test/test2')
PosixPath('test/test1')
PosixPath('test/file1.py')
判断目录或文件是否存在
无论我们给出的路径是否存在,我们都可以使用Path
实例化对象,所以我们可以使用exists()
来判断是否存在
>>> p.exists()
True
>>> p1 = Path('hello')
>>> p1.exists()
False
获得子目录或子文件
我们可以使用is_dir()
和is_file()
来判断Path
对象是否是文件或目录
>>> p
PosixPath('test')
>>> p.is_dir()
True
>>> p.is_file()
False
如果我们想要获得所有的子目录,我们就可以在遍历的时候使用is_dir()
方法
>>> for child in p.iterdir():
... if child.is_dir() :
... print(child)
...
test/test2
test/test1
也许你可以尝试一下生成器表达式,他会更加便捷
>>> files_child = (child for child in p.iterdir() if child.is_file())
>>> for item in files_child:
... print(item)
...
test/file2.py
test/file1.py
获取文件属性
我们可以直接使用.stat()
方法来获取属性,Path.stat()
和os.stat()
十分类似
其中,.stat()
将返回一个os.stat_result
对象
他的几个重要属性:
- st_mode: inode保护模式
- st_ino: unix上inode保护号
- st_dev: 此文件驻留的设备的标识号
- st_nlink:链接数
- st_uid: 所有者的用户ID
- st_gid: 所有者的组ID
- st_size: 文件的大小(以字节为单位)
- st_atim e: 上次访问的时间,以秒为单位
- st_mtime: 最近内容修改的时间, 以秒为单位
- st_ctime: 在unix上是最新的元数据更改的时间,在windows是创建时间
>>> p.stat()
os.stat_result(st_mode=16893, st_ino=8126768, st_dev=66315, st_nlink=4, st_uid=1000, st_gid=1000, st_size=4096, st_atime=1564318502, st_mtime=1564318500, st_ctime=1564318500)
>>> p.stat().st_mtime
1564318500.2075815
创建,删除, 重命名,移动目录或文件
-
Path.mkdir(mode=0o777, parents=False, exist_ok=False)
新建给定路径的目录。
mode: 访问权限,默认是777。
parents: Ture或者False, 默认为False。 如果为True,则,给定的路径中任何找不到的父目录都会随着该路径被创建。如果是False,则如果找不到父级目录会抛出
FileNotFoundError
错误exist_ok: True或者False, 默认为False。 如果为False, 则在目标存在的的情况下,抛出
FileExistsError
,如果为True, 则,如果所给路径最后一个组件不是现存的非目录文件时,则忽略FileExistsError
-
Path.touch(mode=0o666, exist_ok=True)
创建给定路径的文件
mode: 访问权限,默认为666, 每个人都有读和写的权限
exist_ok: True或者False, 默认为True。如果文件已经存在,则当exist_ok为true则函数仍会成功,否则抛出
FileExistsError
。 -
Path.rmdir()
移除目录,目录必须是空的
-
Path.unlink()
移除文件。如果路径指向目录,会抛出
IsADirectoryError
错误 -
Path.rename(target)
使用给定的target将文件重命名
-
Path.chmod(mode)
改变文件的模式和权限
-
Path.replace(target)
将文件名目录重命名为给定的 target, 如果 target 指向一个现有文件或目录,则它将被无条件地替换。
创建目录
在创建目录时, 我们使用Path.mkdir()
方法,这个方法包含三个方法,其中mode
是我们对于所传建文件设置的读写权限,默认是777
, 也就是每个人都有读和写以及执行的权限。
parents
参数可以控制我们是否创建父级目录,如果是True
,我们就可以创建多个目录,如果是False
,则会自动查找父级目录,如果没有父级目录,就会抛出错误。
>>> p1 = Path('test/2019/07/29')
>>> p1.mkdir(parents=True)
>>> exit()
smith@smithgua:~/Desktop$ cd test
smith@smithgua:~/Desktop/test$ tree
.
├── 2019
│ └── 07
│ └── 29
├── file1.py
├── file2.py
├── test1
│ └── file3.py
└── test2
└── file4.py
smith@smithgua:~/Desktop/test$ cd ..
smith@smithgua:~/Desktop$ python
>>> from pathlib import Path
>>> p2 = Path('test/2018/07/29')
>>> p2.mkdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1251, in mkdir
self._accessor.mkdir(self, mode)
FileNotFoundError: [Errno 2] No such file or directory: 'test/2018/07/29'
exist_ok
参数则可以用来解决我们我们所创建的文件已经存在的问题,如果为False
,则如果我们给出的路径是已经存在的,那么就会抛出错误,如果是True
的话,则会忽略这个错误,但同样不再创建目录。但如果我们给出的路径最后一个组件是一个非目录文件,那么无论exist_ok
是什么,都会抛出错误
>>> p1 = Path('test/2019')
>>> p1.mkdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1251, in mkdir
self._accessor.mkdir(self, mode)
FileExistsError: [Errno 17] File exists: 'test/2019'
>>> p1.mkdir(exist_ok=True)
>>> p2 = Path('test/file1.py')
>>> p2.mkdir(exist_ok=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1251, in mkdir
self._accessor.mkdir(self, mode)
FileExistsError: [Errno 17] File exists: 'test/file1.py'
创建文件
在pathlib
模块中,我们使用Path.touch()
方法来创建文件,他包含两个参数,第一个参数是mode
,用来设置权限,第二个参数是exist_ok
,用来处理文件已经存在的情况,在文件已经存在的情况下,如果exist_ok=True
,则函数仍会成功,但在表面上,文件内容没有发生任何改变。如果exist_ok=False
,则抛出FileExistsError
错误。
>>> p2 = Path('test/file1.py')
>>> p2.touch()
>>> p2.touch(exist_ok=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1241, in touch
fd = self._raw_open(flags, mode)
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1048, in _raw_open
return self._accessor.open(self, flags, mode)
FileExistsError: [Errno 17] File exists: 'test/file1.py'
移除目录
我们直接使用Path.rmdir()
方法来移除目录,要保证目录是空的,如果目录是空的,否则抛出OSError
。如果目录不存在,则抛出FileNotFoundError
。
>>> p
PosixPath('test/2019/07/29')
>>> p.rmdir()
>>> p
PosixPath('test/2019/07/29')
>>> p.exists()
False
>>> p_parent = p.parents[2]
>>> p_parent
PosixPath('test')
>>> p_parent.rmdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1295, in rmdir
self._accessor.rmdir(self)
OSError: [Errno 39] Directory not empty: 'test'
>>> p.exists()
False
>>> p.rmdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1295, in rmdir
self._accessor.rmdir(self)
FileNotFoundError: [Errno 2] No such file or directory: 'test/2019/07/29'
移除文件
使用Path.unlink()
来删除文件,但是给出的路径不能是目录,如果是目录则会抛出错误。如果文件不存在,则抛出FileNotFoundError
。
>>> p = Path('test/file1.py')
>>> p.exists()
True
>>> p.unlink()
>>> p.exists()
False
>>> p = Path('test')
>>> p.unlink()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1287, in unlink
self._accessor.unlink(self)
IsADirectoryError: [Errno 21] Is a directory: 'test'
>>> p = Path('test/file1.py')
>>> p.exists()
False
>>> p.unlink()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1287, in unlink
self._accessor.unlink(self)
FileNotFoundError: [Errno 2] No such file or directory: 'test/file1.py'
重命名和移动文件和目录
我们使用Path.rename()
重命名文件,其含有参数target
,将文件名称替换成target
。要保证文件存在,如果文件不存在,则抛出FileNotFoundError
。
Path.replace()
也具有重命名的作用,但是如果原本的文件存在,我们就可以将其无条件替换。
同时,如果target参数给予的是一个路径,我们会将其放在指定的路径下。(但是在有些情况,最后使用重新读出并写入的方式)
我们需要知道的是,虽然更改了名字,但是原来实例化的对象还是没有改变(Python<3.8)。
>>> p = Path('test/file2.py')
>>> p
PosixPath('test/file2.py')
>>> p.exists()
True
>>> p.rename('file1.py')
>>> p
PosixPath('test/file2.py')
>>> p.exists()
False
In [31]: p = Path('bbb.txt')
In [32]: p.exists()
Out[32]: True
In [33]: p.replace('Data/bbb.txt')
In [35]: cd Data
/home/smith/Data
In [36]: ls
bbb.txt ISO/ notebook/ Python/ Tools/ Vm/
改变模式和权限
>>> p = Path('usa.txt')
>>> p.exists()
True
>>> p.chmod(0o777)
>>> p
PosixPath('usa.txt')
>>> p.stat().st_mode
33279
>>> p.chmod(0o444)
>>> p.stat().st_mode
33060
文件名模式匹配
-
Path.glob(pattern)
产生当前目录下所有匹配的文件。
pattern: 通配符,匹配的模式,差不多只是用三种匹配符。 1. ‘*‘匹配0或多个字符 2. ‘?’匹配单个字符 3. ‘[]’匹配制定范围内的字符,如:[0-9]匹配数字。
模式匹配
当我们需要搜索和特定的模式匹配的文件时,在pathlib
模块中,提供了Path.glob()
方法,我们只使用了三种匹配符。
Path.glob()
返回了给定目录下所有匹配的该模式的生成器对象
>>> p
PosixPath('test')
>>> p.glob('*.py')
<generator object Path.glob at 0x7fe32a25bf48>
>>> list(p.glob('*.py'))
[PosixPath('test/file2.py'), PosixPath('test/file1.py')]
>>> list(p.glob('?ile1.py'))
[PosixPath('test/file1.py')]
>>> list(p.glob('file[0-9].py'))
[PosixPath('test/file2.py'), PosixPath('test/file1.py')]
文件名拼接
-
Path.joinpath(*other)
将other参数中的项目链接在一起
- other: 多个表示路径的字符串或Path对象
文件名拼接
Path.joinpath()
方法只是单纯的将其拼接起来,无论你给other参数多少个对象,他都会以此拼接得到一个路径。
>>> p.joinpath('test1', 'test2')
PosixPath('test/test1/test2')
需要注意的是,如果other
参数中含有以/
开头的路径,路径将从最后一个以/
开头的路径开始拼接
>>> p.joinpath('test2', '/test3')
PosixPath('/test3')
文件的读写操作
-
Path.open(mode=’r’, buffering=-1, encoding=None, errors=None, newline=None)
用于打开文件,具体参数可以参见观望内置函数open(),因为内容太多了,我懒得写,大多我暂时又用不到。
-
Path.read_bytes()
以字节对象的形式返回路径指向的文件的二进制内容
-
Path.read_text()
以字符串形式返回路径指向的文件的解码后的文本内容
-
Path.write_bytes(data)
将文件以文本模式打开,写入data并关闭
-
Path.write_text(data, encoding=None, errors=None)
将文件以文本模式打开,写入data并关闭。
encoding: 编码模式
打开文件
pathlib模块有一个Path.open()
的方法,用来打开路径指向的文件,他就跟python内置函数open()
函数所做的是一样。
Path.open()含有五个参数,其中mode
参数控制了打开的模式, 默认是 ‘r’,也就是打开并读取。
smith@smithgua:~/Desktop$ cat test/file1.py
def test():
print('hello world')
smith@smithgua:~/Desktop$ python
>>> from pathlib import Path
>>>
>>> p = Path('test/file1.py')
>>> p
PosixPath('test/file1.py')
>>> p.exists()
True
>>> with p.open() as f:
... f.read()
...
"def test():\n print('hello world')\n"
>>> with p.open(mode='a') as f:
... f.write('hello world\n')
...
11
>>> exit()
smith@smithgua:~/Desktop$ cat test/file1.py
def test():
print('hello world')
hello world
读取文件内容
Pathlib模块提供了读取整个文件的方法,也就是Path.read_bytes()
和Path.read_text()
方法,一种是以字节对象的形式返回文件的二进制内容,一种是以字符串形式返回文件中解码后的文本内容。
与常规的python对文件的操作,使用pathlib方法,我们不需要再打开文件,而是直接对Path对象操作即可
>>> str1 = p.read_bytes()
>>> str1
b"def test():\n print('hello world')\nhello world\n"
>>> type(str1)
<class 'bytes'>
>>> str2 = p.read_text()
>>> str2
"def test():\n print('hello world')\nhello world\n"
>>> type(str2)
<class 'str'>
写入文件
Pathlib模块提供了Path.write_bytes()
和Path.write_text()
来写入内容。这两种方法一个是以文件以二进制模式打开,一种是以文本模式打开。
使用Path.write_bytes()
方法则需要注意格式,我们的data
不是一个字符串形式的。
Path.write_text()
含有三个参数,data
就是你写入的内容,encoding
是你的编码格式。
需要注意的是,我们使用Pathlib模块提供的方法写入内容是直接覆盖的,是不可以选择写入模式的。
smith@smithgua:~/Desktop$ cat test/file1.py
def test():
print('hello world')
hello world
smith@smithgua:~/Desktop$ python
Python 3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pathlib import Path
>>>
>>> p = Path('test/file1.py')
>>>
>>> p.write_bytes('hello world')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/smith/anaconda3/lib/python3.7/pathlib.py", line 1207, in write_bytes
view = memoryview(data)
TypeError: memoryview: a bytes-like object is required, not 'str'
>>> p.write_bytes(b'hello world')
11
>>> exit()
smith@smithgua:~/Desktop$ cat test/file1.py
hello world
smith@smithgua:~/Desktop$ cat test/file1.py
hello worldsmith@smithgua:~/Desktop$ python
Python 3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pathlib import Path
>>> p = Path('test/file1.py')
>>> p.write_text('hello python\n')
13
>>>
>>> exit()
smith@smithgua:~/Desktop$ cat test/file1.py
hello python
与IO模块的结合
虽然pathlib模块提供的读写方法都很强大,但是依然有所不足,所以,与IO模块中对文件的操作进行结合是相当适合的。