目录
- 一、文件IO操作
-
- [1.1 创建或打开文件](#1.1 创建或打开文件)
- [1.2 读取文件](#1.2 读取文件)
-
- [1.2.1 按行读取](#1.2.1 按行读取)
- [1.2.2 多行读取](#1.2.2 多行读取)
- [1.2.3 完整读取](#1.2.3 完整读取)
- [1.3 写入文件](#1.3 写入文件)
-
- [1.3.1 写入字符串](#1.3.1 写入字符串)
- [1.3.2 写入序列](#1.3.2 写入序列)
- [1.4 上下文管理](#1.4 上下文管理)
-
- [1.4.1 with语句的使用](#1.4.1 with语句的使用)
- [1.4.2 上下文管理器(拓展----可以学了面向对象之后再回来看)](#1.4.2 上下文管理器(拓展----可以学了面向对象之后再回来看))
- [1.5 文件的遍历](#1.5 文件的遍历)
- 二、os.path模块
- 三、pathlib模块以及shutil模块
-
- [3.1 pathlib模块](#3.1 pathlib模块)
- [3.2 shutil模块](#3.2 shutil模块)
- 四、csv文件
一、文件IO操作
Python 内置了文件对象,通过 open() 函数可以获取一个文件对象,再通过文件对象的各种方法可以实现对文件的读、写、删除等基本操作。
1.1 创建或打开文件
在全局作用域中,Python3 移除了内置的 file() 函数,把该函数创建文件的功能集成到了 open() 函数中,使用 open() 函数可以打开或创建文件,基本用法如下:
python
In [1]: open?
Signature:
open(
file,
mode='r',
buffering=-1,
encoding=None,
errors=None,
newline=None,
closefd=True,
opener=None,
)
Docstring:
Open file and return a stream. Raise OSError upon failure.
file is either a text or byte string giving the name (and the path
if the file isn't in the current working directory) of the file to
be opened or an integer file descriptor of the file to be
wrapped. (If a file descriptor is given, it is closed when the
returned I/O object is closed, unless closefd is set to False.)
mode is an optional string that specifies the mode in which the file
is opened. It defaults to 'r' which means open for reading in text
mode. Other common values are 'w' for writing (truncating the file if
it already exists), 'x' for creating and writing to a new file, and
'a' for appending (which on some Unix systems, means that all writes
append to the end of the file regardless of the current seek position).
In text mode, if encoding is not specified the encoding used is platform
dependent: locale.getpreferredencoding(False) is called to get the
current locale encoding. (For reading and writing raw bytes use binary
mode and leave encoding unspecified.) The available modes are:
========= ===============================================================
Character Meaning
--------- ---------------------------------------------------------------
'r' open for reading (default)
'w' open for writing, truncating the file first
'x' create a new file and open it for writing
'a' open for writing, appending to the end of the file if it exists
'b' binary mode
't' text mode (default)
'+' open a disk file for updating (reading and writing)
'U' universal newline mode (deprecated)
========= ===============================================================
The default mode is 'rt' (open for reading text). For binary random
access, the mode 'w+b' opens and truncates the file to 0 bytes, while
'r+b' opens the file without truncation. The 'x' mode implies 'w' and
raises an `FileExistsError` if the file already exists.
Python distinguishes between files opened in binary and text modes,
even when the underlying operating system doesn't. Files opened in
binary mode (appending 'b' to the mode argument) return contents as
bytes objects without any decoding. In text mode (the default, or when
't' is appended to the mode argument), the contents of the file are
returned as strings, the bytes having been first decoded using a
platform-dependent encoding or using the specified encoding if given.
'U' mode is deprecated and will raise an exception in future versions
of Python. It has no effect in Python 3. Use newline to control
universal newlines mode.
buffering is an optional integer used to set the buffering policy.
Pass 0 to switch buffering off (only allowed in binary mode), 1 to select
line buffering (only usable in text mode), and an integer > 1 to indicate
the size of a fixed-size chunk buffer. When no buffering argument is
given, the default buffering policy works as follows:
* Binary files are buffered in fixed-size chunks; the size of the buffer
is chosen using a heuristic trying to determine the underlying device's
"block size" and falling back on `io.DEFAULT_BUFFER_SIZE`.
On many systems, the buffer will typically be 4096 or 8192 bytes long.
* "Interactive" text files (files for which isatty() returns True)
use line buffering. Other text files use the policy described above
for binary files.
encoding is the name of the encoding used to decode or encode the
file. This should only be used in text mode. The default encoding is
platform dependent, but any encoding supported by Python can be
passed. See the codecs module for the list of supported encodings.
errors is an optional string that specifies how encoding errors are to
be handled---this argument should not be used in binary mode. Pass
'strict' to raise a ValueError exception if there is an encoding error
(the default of None has the same effect), or pass 'ignore' to ignore
errors. (Note that ignoring encoding errors can lead to data loss.)
See the documentation for codecs.register or run 'help(codecs.Codec)'
for a list of the permitted encoding error strings.
newline controls how universal newlines works (it only applies to text
mode). It can be None, '', '\n', '\r', and '\r\n'. It works as
follows:
* On input, if newline is None, universal newlines mode is
enabled. Lines in the input can end in '\n', '\r', or '\r\n', and
these are translated into '\n' before being returned to the
caller. If it is '', universal newline mode is enabled, but line
endings are returned to the caller untranslated. If it has any of
the other legal values, input lines are only terminated by the given
string, and the line ending is returned to the caller untranslated.
* On output, if newline is None, any '\n' characters written are
translated to the system default line separator, os.linesep. If
newline is '' or '\n', no translation takes place. If newline is any
of the other legal values, any '\n' characters written are translated
to the given string.
If closefd is False, the underlying file descriptor will be kept open
when the file is closed. This does not work when a file name is given
and must be True in that case.
A custom opener can be used by passing a callable as *opener*. The
underlying file descriptor for the file object is then obtained by
calling *opener* with (*file*, *flags*). *opener* must return an open
file descriptor (passing os.open as *opener* results in functionality
similar to passing None).
open() returns a file object whose type depends on the mode, and
through which the standard file operations such as reading and writing
are performed. When open() is used to open a file in a text mode ('w',
'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open
a file in a binary mode, the returned class varies: in read binary
mode, it returns a BufferedReader; in write binary and append binary
modes, it returns a BufferedWriter, and in read/write mode, it returns
a BufferedRandom.
It is also possible to use a string or bytearray as a file for both
reading and writing. For strings StringIO can be used like a file
opened in a text mode, and for bytes a BytesIO can be used like a file
opened in a binary mode.
Type: function
open() 函数共包含 8 个参数,比较重要的是前 4 个参数,除了 file 参数外,其他参数都有默认值,可以省略。参数说明如下:
- file:必须,指定要打开的文件名称或文件句柄,文件名称包含所在的路径(相对或者绝对路径)
- mode:打开模式,即文件打开权限。默认值为
'rt'
。文件的打开模式有十几种,简单说明如下所示:- 文件格式相关参数:本组参数可以与其他模式参数组合使用,用于指定打开文件的格式,需要根据要打开文件的类型进行选择
t
: 文本模式,默认以文本格式打开文件,一般用于文本文件b
: 二进制模式,以二进制格式打开文件,一般用于非文本文件,如图片等
- 通用读写模式相关参数:本组参数可以与文件格式参数组合使用,用于设置基本读、写操作权限,以及文件指针初始位置
'r'
: 只读模式。默认。以只读方式打开一个文件,文件指针被定位到文件头的位置。如果该文件不存在,则会报错'w'
: 只写模式。打开一个文件只用于写入。如果该文件已存在,则打开文件,清空文件内容,并把文件指针定位到文件头位置开始编辑。如果该文件不存在,则创建新文件,打开并编辑'a'
: 追加模式。打开一个文件用于追加,仅有只写权限,无权读操作。如果该文件已存在,文件指针被定位到文件尾。新内容被写入到原内容之后。如果该文件不存在,则创建新文件并写入
- 特殊读写模式相关参数
'+'
: 更新模式。打开一个文件进行更新,具有可读、可写权限。注意,该模式不能单独使用,需要与r/w/a
模式组合使用。打开文件后,文件指针的位置由r/w/a
组合模式决定'x'
: 只写模式。新建一个文件,打开并写入内容,如果该文件已存在,则会报错
- 组合模式:文件格式与通用读写模式可以组合使用,另外通过组合+模式可以为只读、只写模式增加写、读的权限
'r+'
: 文本格式读写。以文本格式打开一个文件用于读、写。文件指针被定位到文件头的位置。新写入的内容将覆盖掉原有文件部分或全部内容;如果该文件不存在,则会报错'rb'
: 二进制格式只读。以二进制格式打开一个文件,只能够读取。文件指针被定位到文件头的位置。一般用于非文本文件,如图片等'rb+'
: 二进制格式读写。以二进制格式打开一个文件用于读、写。文件指针被定位到文件头的位置。新写入的内容将覆盖掉原有文件部分或全部内容;如果该文件不存在,则会报错。一般用于非文本文件'w+'
: 文本格式写读。以文本格式打开一个文件用于写、读。如果该文件已存在,则打开文件,清空原有内容,进入编辑模式。如果该文件不存在,则创建新文件,打开并执行写、读操作'wb'
: 二进制格式只写。以二进制格式打开一个文件,只能够写入。如果该文件已存在,则打开文件,清空原有内容,进入编辑模式。如果该文件不存在,则创建新文件,打开并执行只写操作。一般用于非文本文件'wb+'
: 二进制格式写读。以二进制格式打开一个文件用于写、读。如果该文件已存在,则打开文件,清空原有内容,进入编辑模式。如果该文件不存在,则创建新文件,打开并执行写、读操作。一般用于非文本文件'a+'
: 文本格式读写。以文本格式打开一个文件用于读、写。如果该文件已存在,则打开文件,文件指针被定位到文件尾的位置,新写入的内容添加在原有内容的后面。如果该文件不存在,则创建新文件,打开并执行写、读操作'ab'
: 二进制格式只写。以二进制格式打开一个文件用于追加写入。如果该文件已存在,则打开文件,文件指针被定位到文件尾的位置,新写入的内容在原有内容的后面。如果该文件不存在,则创建新文件,打开并执行只写操作'ab+'
: 二进制格式读写。以二进制格式打开一个文件用于追加写入。如果该文件已存在,则打开文件,文件指针被定位到文件尾的位置,新写入的内容在原有内容的后面。如果该文件不存在,则创建新文件,打开并执行写、读操作
- ps: 以二进制模式打开的文件(包含
'b'
),返回文件内容为字节对象,而不进行任何解码。在文本模式(包含't'
时,返回文件内容为字符串,已经解码。
- 文件格式相关参数:本组参数可以与其他模式参数组合使用,用于指定打开文件的格式,需要根据要打开文件的类型进行选择
- buffering:设置缓冲方式。0表示不缓冲,直接写入磁盘;1表示行缓冲,缓冲区碰到
\n
换行符时写入磁盘;如果为大于1的正整数,则缓冲区文件大小达到该数字大小时,写入磁盘。如果为负值,则缓冲区的缓冲大小为系统默认。 - encoding:指定文件的编码方式,默认为 utf-8。该参数只在文本模式下使用。
- errors:报错级别。
- newline:设置换行符(仅适用于文本模式)。
- closefd:布尔值,默认为 True,表示 file 参数为文件名(字符串型);如果为 False,则 file 参数为文件描述符
- opener:传递可调用对象
示例1:如果需要创建一个新的文件,在 open() 函数中可以使用 w+
模式,用 w+
模式打开文件时,如果该文件不存在,则会创建该文件,而不会抛出异常。
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 8:16
# @Author : AmoXiang
# @File: open_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
file_name = 'test.txt' # 创建的文件名
# 建议,使用文件对象时,一定要指定编码,而不是使用默认编码
fp = open(file_name, mode='w+', encoding='utf8') # 创建文件
print(f'{file_name} 文件创建成功')
fp.close() # 关闭文件
示例2:r
模式只能打开已存在的文件,当打开不存在的文件时,open() 函数会抛出异常。
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 8:16
# @Author : AmoXiang
# @File: open_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
file_name = 'test1.txt' # 创建的文件名
# 建议,使用文件对象时,一定要指定编码,而不是使用默认编码
fp = open(file_name, mode='r', encoding='utf8') # 创建文件
'''
FileNotFoundError: [Errno 2] No such file or directory: 'test1.txt'
下面的代码都不会执行,所以文件对象无法关闭,我们可以使用异常进行处理,
关于异常后续文章会进行详细讲解,这里不进行赘述
'''
print(f'{file_name} 文件创建成功')
fp.close() # 关闭文件
1.2 读取文件
调用 open() 函数后,将会返回一个 file 对象,file 对象包含很多方法,使用这些方法可以对打开的文件进行读写操作。文件的读取有多种方法,可以使用 file 对象的 readline()、readlines() 或 read() 方法读取文件。下面具体介绍这些函数的用法。
1.2.1 按行读取
使用 readline() 方法可以每次读取文件中的一行,包括 "\n"
字符。当文件指针移动到文件的末尾时,如果继续使用 readline() 读取文件,将抛出异常,因此在逐行读取时需要使用条件语句,判断文件指针是否移动到文件的尾部,以便及时中断循环。
【示例1】本示例演示了 readline() 方法的使用。新建文本文件,保存为 test.txt,然后输入下面多行字符串,它们是 file 对象的可用方法。
text
file.close(): 关闭文件。
file.flush(): 刷新文件。
file.fileno(): 返回文件描述符。
file.isatty(): 判断文件是否连接到终端设备。
file.next(): 返回下一行。
file.read([size]): 读取指定字节数。
file.readline([size]): 读取整行。
file.readlines([sizeint]): 读取所有行。
file.seek(offset[,whence]): 设置当前位置。
file.tell(): 返回当前位置。
file.truncate([size]): 截取文件。
file.write(str): 写入文件。
file.writelines(sequence): 写入序列字符串。
最后,使用下面代码逐行读取 test.txt 文件中的字符串并输出显示,示例代码如下:
python
In [1]: # -*- coding: utf-8 -*-
...: # @Time : 2024-09-19 8:16
...: # @Author : AmoXiang
...: # @File: open_demo.py
...: # @Software: PyCharm
...: # @Blog: https://blog.csdn.net/xw1680
...:
...: f = open("test.txt", mode="r", encoding="utf8") # 打开文本文件
...: while True: # 执行无限循环
...: line = f.readline() # 读取每行文本
...: if line: # 如果不是尾行,则显示读取的文本
...: print(line)
...: else: # 如果是尾行,则跳出循环
...: break
...: f.close() # 关闭文件对象
file.close(): 关闭文件。
file.flush(): 刷新文件。
file.fileno(): 返回文件描述符。
file.isatty(): 判断文件是否连接到终端设备。
file.next(): 返回下一行。
file.read([size]): 读取指定字节数。
file.readline([size]): 读取整行。
file.readlines([sizeint]): 读取所有行。
file.seek(offset[,whence]): 设置当前位置。
file.tell(): 返回当前位置。
file.truncate([size]): 截取文件。
file.write(str): 写入文件。
file.writelines(sequence): 写入序列字符串。
Out[1]: <function TextIOWrapper.close()>
readline() 方法包含一个可选参数,设置从文件中读取的字节数。如果把上面示例中第10行代码改为如下语句,读取的方式略有不同,但读取的内容完全相同。该行代码并不表示每行只读取5字节的内容,而是指每行每次读5字节,直到行的末尾。
python
In [4]: f = open('test.txt', mode='r', encoding='utf8')# 打开文本文件
...: while True: # 执行无限循环
...: line = f.readline(5) # 每次读取每行5个字节
...: if line: # 如果不是尾行,则显示读取的文本
...: print(line)
...: else: # 如果是尾行,则跳出循环
...: break
...: f.close() # 关闭文件对象
file.
close
(): 关
闭文件。
file.
flush
(): 刷
新文件。
file.
filen
o():
返回文件描
述符。
file.
isatt
y():
判断文件是
否连接到终
端设备。
file.
next(
): 返回
下一行。
file.
read(
[size
]): 读
取指定字节
数。
file.
readl
ine([
size]
): 读取
整行。
file.
readl
ines(
[size
int])
: 读取所
有行。
file.
seek(
offse
t[,wh
ence]
): 设置
当前位置。
file.
tell(
): 返回
当前位置。
file.
trunc
ate([
size]
): 截取
文件。
file.
write
(str)
: 写入文
件。
file.
write
lines
(sequ
ence)
: 写入序
列字符串。
Out[4]: <function TextIOWrapper.close()>
1.2.2 多行读取
使用 readlines() 方法可以一次性读取文件中的多行数据,然后返回一个列表,用户可以通过循环访问列表中的元素。
【示例 2】示例代码演示了 readlines() 读取文件的方法。
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 8:16
# @Author : AmoXiang
# @File: open_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
f = open('test.txt', mode='r', encoding='utf8') # 打开文本文件
lines = f.readlines() # 读取所有行
for line in lines:
print(line) # 从列表中读取每行并显示
f.close() # 关闭文件对象
在上面的代码中,第9行代码调用了 readlines() 方法,把文件 test.txt 中包含的所有字符串都读取出来;第10行代码循环读取列表 lines 中的内容;第11行代码输出列表 lines 中每个元素的内容,最后手动关闭文件。
1.2.3 完整读取
读取文件最简单的方法是使用 read() 方法,它能够从文件中一次性读出所有内容,并赋值给1个字符串变量。
【示例3】本示例演示了 read() 读取文件的方法。
python
In [5]: # -*- coding: utf-8 -*-
...: # @Time : 2024-09-19 8:16
...: # @Author : AmoXiang
...: # @File: open_demo.py
...: # @Software: PyCharm
...: # @Blog: https://blog.csdn.net/xw1680
...:
...: f = open('test.txt', mode='r', encoding='utf8') # 打开文本文件
...: content = f.read() # 读所有内容
...: print(content) # 显示所有内容
...: f.close() # 关闭文件对象
file.close(): 关闭文件。
file.flush(): 刷新文件。
file.fileno(): 返回文件描述符。
file.isatty(): 判断文件是否连接到终端设备。
file.next(): 返回下一行。
file.read([size]): 读取指定字节数。
file.readline([size]): 读取整行。
file.readlines([sizeint]): 读取所有行。
file.seek(offset[,whence]): 设置当前位置。
file.tell(): 返回当前位置。
file.truncate([size]): 截取文件。
file.write(str): 写入文件。
file.writelines(sequence): 写入序列字符串。
在上面的示例代码中,调用 read() 方法把文件 test.txt 中所有的内容存储在变量 content 中,然后输出显示所有文件包含的内容。read() 方法包含一个可选的参数,用来设置返回指定字节的内容。
【示例 4】使用 read() 方法分两次从 test.txt 文件中读取 13 字节和 5 字节内容。
python
In [6]: # -*- coding: utf-8 -*-
...: # @Time : 2024-09-19 8:16
...: # @Author : AmoXiang
...: # @File: open_demo.py
...: # @Software: PyCharm
...: # @Blog: https://blog.csdn.net/xw1680
...:
...: f = open('test.txt', mode='r', encoding='utf8') # 打开文本文件
...: str1 = f.read(13) # 读取14个字节内容
...: print(str1) # 显示内容
...: print(f.tell()) # 获取文件对象的当前指针位置
...: str1 = f.read(5) # 读取5个字节内容
...: print(str1) # 显示内容
...: print(f.tell()) # 获取文件对象的当前指针位置
...: f.close() # 关闭文件对象
file.close():
15
关闭文件
28
第 1 次调用 f.read(13) 读取文本文件中的 13 个字符,此时当前指针位置下移到第 14 字节位置。第 2 次调用 f.read(5) 读取文本文件中的 5 个字符,此时当前指针位置下移到第 23 字节位置。由于拉丁字符都是单字符字节,所以 1 个字符等于 1 字节,而汉字都是双字节字符,1 个汉字等于 2 字节。file 对象内部将记录文件指针的位置,以便下次操作。只要 file 对象没有执行 close() 方法,文件指针就不会释放,也可以使用 file 对象的 seek() 方法设置当前指针位置。用法如下:
python
In [8]: f.seek?
Signature: f.seek(cookie, whence=0, /)
Docstring:
Change stream position.
Change the stream position to the given byte offset. The offset is
interpreted relative to the position indicated by whence. Values
for whence are:
* 0 -- start of stream (the default); offset should be zero or positive
* 1 -- current stream position; offset may be negative
* 2 -- end of stream; offset is usually negative
Return the new absolute position.
Type: builtin_function_or_method
参数说明:
1.f 表示文件对象。
2.参数 cookie 表示需要移动偏移的字节数。
3.参数 whence 表示偏移参照点,默认值为0,表示文件的开头;当值为1时,表示当前位置;当值为2时,表示文件的结尾。
【示例 5】使用 read() 方法分两次从 test.txt 文件中读取第1行的 file.close() 和第2行的 file.flush()。
python
In [9]: # -*- coding: utf-8 -*-
...: # @Time : 2024-09-19 8:16
...: # @Author : AmoXiang
...: # @File: open_demo.py
...: # @Software: PyCharm
...: # @Blog: https://blog.csdn.net/xw1680
...:
...: f = open('test.txt', mode='rb') # 使用b模式选项打开文本文件
...: str1 = f.read(12) # 读取12个字节内容
...: print(str1) # 显示内容
...: f.seek(15, 1) # 设置指针以当前位置为参照向后偏移15个字节
...: str1 = f.read(12) # 读取12个字节内容
...: print(str1) # 显示内容
...: print(f.tell()) # 获取文件对象的当前指针位置
...: f.close() # 关闭文件对象
b'file.close()'
b'\xb6\xe3\x80\x82\r\nfile.f'
39
1.3 写入文件
使用文件对象的 write() 和 writelines() 方法可以为文件写入内容。
1.3.1 写入字符串
write() 方法能够将传入的字符串写入文件,并返回写入的字符长度。语法格式:
python
In [10]: f.write?
Docstring:
Write the given buffer to the IO stream.
Returns the number of bytes written, which is always the length of b
in bytes.
Raises BlockingIOError if the buffer is full and the
underlying raw stream cannot accept more data at the moment.
Type: builtin_function_or_method
【示例 1】使用 open() 函数以 w 模式创建并打开 test1.txt 文件,然后在文件中写入字符串 "Python"
。
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 8:16
# @Author : AmoXiang
# @File: open_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
f = open("test1.txt", mode="w", encoding='utf8') # 打开文件
content = "Python" # 定义字符串
n = f.write(content) # 写入字符,
print(n) # 显示写入字符长度
f.close() # 关闭文件
上述方法在写入前会清除文件中原有的内容,再重新写入新的内容,相当于 "覆盖"
的方式。如果需要保留文件中原有的内容,只是添加新的内容,可以使用 "a"
模式打开文件。
1.3.2 写入序列
writelines() 方法能够将一个序列的字符串写入文件。语法格式如下:
python
In [11]: f.writelines?
Signature: f.writelines(lines, /)
Docstring:
Write a list of lines to stream.
Line separators are not added, so it is usual for each of the
lines provided to have a line separator at the end.
Type: builtin_function_or_method
【示例 2】使用 writelines() 方法把字符串列表写入打开的 test2.txt 文件。
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 8:16
# @Author : AmoXiang
# @File: open_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
f = open("test2.txt", "w", encoding='utf8') # 打开文件
w_list = ["Python", "Java", "C"] # 定义字符串列表
f.writelines(w_list) # 写入字符串列表
w_list = ["\nPython", "\nJava", "\nC"] # 定义字符串列表,添加换行符
f.writelines(w_list) # 写入字符串列表
f.close() # 关闭文件
writelines() 方法不会换行写入每个元素,如果换行写入每个元素,就需要手动添加换行符 \n
。 使用 writelines() 方法写入文件的速度更快。如果需要写入文件的字符串非常多,可以使用 writelines() 方法提高效率。如果只需要写入少量的字符串,则直接使用 write() 方法即可。
1.4 上下文管理
如果你有阅读源码的习惯,可能会看到一些优秀的代码经常出现带有 with
关键字的语句,它通常用在什么场景呢?对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源。
比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 Too many open files
的错误,因为系统允许你打开的最大文件数量是有限的。同样,对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 Can not connect to MySQL server Too many connections
,因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。
1.4.1 with语句的使用
向文件中写入数据的示例代码(基础):
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 10:09
# @Author : AmoXiang
# @File: with_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
# 1、以写的方式打开文件
f = open("1.txt", "w", encoding="utf8")
# 2、写入文件内容
f.write("hello world")
# 3、关闭文件
f.close()
代码说明如下:文件使用完后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的。这种写法可能出现一定的安全隐患,错误代码如下:
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 10:09
# @Author : AmoXiang
# @File: with_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
# 1、以写的方式打开文件
f = open("1.txt", "r", encoding="utf8")
# 2、写入文件内容
f.write("hello world")
# 3、关闭文件
f.close()
运行结果如下图所示:
代码说明:由于文件读写时都有可能产生 IOError
,一旦出错,后面的 f.close()
就不会调用。为了保证无论是否出错都能正确地关闭文件,我们可以使用 try ... finally
来解决。安全写法, 代码如下:
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 10:09
# @Author : AmoXiang
# @File: with_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
try:
# 1、以读的方式打开文件
f = open("1.txt", "r", encoding='utf8')
# 2、读取文件内容
f.write("xxxxx")
except IOError as e:
print("文件操作出错", e)
finally:
# 3、关闭文件
f.close()
运行结果:
text
文件操作出错 not writable
这种方法虽然代码运行良好,但是缺点就是代码过于冗长,并且需要添加 try-except-finally
语句,不是很方便,也容易忘记。在这种情况下,Python 提供了 with 语句的这种写法,既简单又安全,并且 with 语句执行完成以后自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作。语法格式:
python
with 文件对象 as 标识符: # 等同于 标识符 = 文件对象
pass # 标识符可以在内部使用
说明:
上下文管理
1.使用with关键字,上下文管理针对的是with后的对象
2.使用with ... as关键字
3.上下文管理的语句块并不会开启新的作用域
文件对象上下文管理
1.进入with时,with后的文件对象是被管理对象
2.as子句后的标识符,指向with后的文件对象
3.with语句块执行完的时候,会自动关闭文件对象
with 语句的示例代码:
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 10:09
# @Author : AmoXiang
# @File: with_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
filename = './test.txt'
file = open(filename, mode='r', encoding='utf8')
with file as f:
print(f is file)
print(1, f.closed)
print(f.write('abcd')) # r模式写入失败
print(2, f.closed) # with中不管是否抛异常,with结束时都会关闭文件对象
1.4.2 上下文管理器(拓展----可以学了面向对象之后再回来看)
一个类只要实现了 __enter__()
和 __exit__()
这个两个方法,通过该类创建的对象我们就称之为上下文管理器对象。上下文管理器可以使用 with 语句,with 语句之所以这么强大,背后是由上下文管理器做支撑的,也就是说刚才使用 open 函数创建的文件对象就是就是一个上下文管理器对象。自定义上下文管理器类,模拟文件操作。定义一个 File 类,实现 __enter__()
和 __exit__()
方法,然后使用 with 语句来完成操作文件, 示例代码:
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 10:20
# @Author : AmoXiang
# @File: demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
class File(object):
# 初始化方法
def __init__(self, file_name, mode):
# 定义变量保存文件名和打开模式
self.file_name = file_name
self.mode = mode
# 上文方法
def __enter__(self):
print("进入上文方法")
# 返回文件资源
self.f = open(self.file_name, self.mode)
return self.f
# 下文方法
def __exit__(self, exc_type, exc_val, exc_tb):
"""
with 语句中,即使发生异常信息,也会进入 __exit__ 中
:param exc_type: 发生异常时,异常的类型
:param exc_val: 发生异常时,异常的信息
:param exc_tb: 异常对象,堆栈信息
:return:如果返回False(代码没有书写返回值默认None,表示为False) 代表
异常发生以后,异常会继续向外传递。如果返回True,代表异常不再向外传递
"""
if exc_type:
print("发生了异常......")
return True
print("进入下文方法")
self.f.close()
if __name__ == '__main__':
with File("1.txt", "r") as file:
content = file.read()
print(content)
运行结果:
代码说明:
__enter__
表示上文方法,需要返回一个操作文件对象__exit__
表示下文方法,with 语句执行完成会自动执行,即使出现异常也会执行该方法。
上下文管理器的另外一种实现方式: 假如想要让一个函数成为上下文管理器,Python 还提供了一个 @contextmanager
的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 上面的语句在 __enter__
方法中执行,yield 下面的语句在 __exit__
方法中执行,紧跟在 yield 后面的参数是函数的返回值。示例代码:
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 10:21
# @Author : AmoXiang
# @File: demo2.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
# 导入装饰器
from contextlib import contextmanager
# 装饰器装饰函数,让其称为一个上下文管理器对象
@contextmanager
def my_open(path, mode):
try:
# 打开文件
file = open(path, mode)
# yield之前的代码好比是上文方法
yield file
except Exception as e:
print(e)
finally:
print("over")
# yield下面的代码好比是下文方法
file.close()
# 使用with语句
with my_open('out.txt', 'w') as f:
f.write("hello , the simplest context manager")
1.5 文件的遍历
类似于日志文件,文件需要遍历,最常用的方式就是逐行遍历。示例:
python
# -*- coding: utf-8 -*-
# @Time : 2024-09-19 10:23
# @Author : AmoXiang
# @File: 文件的遍历.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
filename = './test3.txt'
with open(filename, 'w', encoding='utf8') as f:
f.write('\n'.join(map(str, range(101, 120))))
with open(filename, mode='r', encoding='utf8') as f:
for line in f: # 文件对象是可迭代对象,逐行遍历
print(line.encode()) # 带换行符