398 lines
17 KiB
Markdown
398 lines
17 KiB
Markdown
# Python 读写文件:示例
|
||
|
||
> 原文:[https://python.land/operating-system/python-files](https://python.land/operating-system/python-files)
|
||
|
||
文件是使用计算机的重要组成部分,因此使用 Python 读写文件是您需要掌握的基本技能。在本文中,我将向您展示如何完成您的目标,例如:
|
||
|
||
* 如何在 Python 中打开文件
|
||
* 用 Python 读取文件(一次或逐行)
|
||
* 用 Python 写文件
|
||
* 复制、移动、重命名和删除文件
|
||
* 检查文件或目录是否存在
|
||
|
||
当处理文件时,你需要了解*文件模式和权限*。如果你需要的话,我也包括了对这个主题的解释。
|
||
|
||
目录
|
||
|
||
|
||
|
||
* [用 Python 打开一个文件](#Open_a_file_in_Python "Open a file in Python")
|
||
* [Python 写文件](#Python_write_file "Python write file")
|
||
* [Python 追加到文件](#Python_append_to_a_file "Python append to a file")
|
||
* [Python 读取文件到列表](#Python_read_file_to_list "Python read file to list")
|
||
* [常见的 Python 文件操作](#Common_Python_file_operations "Common Python file operations")
|
||
* [处理 Python 文件异常](#Handling_Python_file_exceptions "Handling Python file exceptions")
|
||
* [关于 Unix 文件权限](#About_Unix_file_permissions "About Unix file permissions")
|
||
* [继续学习](#Keep_learning "Keep learning")
|
||
|
||
|
||
|
||
## 用 Python 打开一个文件
|
||
|
||
在 Python 中,我们用`open()` [函数](https://python.land/introduction-to-python/functions)打开一个文件。这是 Python 内置函数的一部分,你不需要[导入](https://python.land/project-structure/python-modules)任何东西来使用`open()`。open()函数至少需要一个参数:文件名。如果文件成功打开,它将返回一个 file 对象,您可以使用该对象读取和写入该文件。
|
||
|
||
当你用 Python 打开一个文件时,你正在使用系统资源,一旦你完成了,你需要释放这些资源。如果不这样做,就会造成所谓的资源泄漏。如果您经常这样做,您可能会遇到问题,因为普通操作系统用户可以打开的文件数量是有限的。
|
||
|
||
不过这不一定是个问题。当您的程序退出时,所有资源都会自动释放。Python 开发者的想法很好,也很体贴,但是很多软件都是无限期运行的。有了这样的软件,你需要更加小心,关闭你不再需要的资源。这样,您可以确保其他软件可以安全地访问该文件。此外,当您打开许多文件时,每个打开的文件都会占用内存和其他资源。由于这些原因,当你完成一个文件时,关闭它是一个好习惯。
|
||
|
||
关闭文件曾经是一项手动任务,很容易被忘记。幸运的是,Python 有‘with’语句来帮助我们。我们将很快讨论`with`声明。我首先要向你展示“老式的做事方法”。
|
||
|
||
### 打开文件的老式方法
|
||
|
||
为了说明 with 语句如此有用的原因,让我们首先以传统的手动方式打开和关闭一个文件:
|
||
|
||
[https://crumb . sh/embed/aetlu 9 ifs](https://crumb.sh/embed/aetLU9iFNZs)
|
||
|
||
打开文件的老式方法。您应该更喜欢使用 with 语句。
|
||
|
||
如果你想冒险,试着去掉`f.close()`。它应该是一样的;Python 不会抱怨,会在退出时关闭文件资源。
|
||
|
||
如果上面的交互式示例由于某种原因无法运行,下面是相同代码的文本版本:
|
||
|
||
```py
|
||
f = open('text.txt')
|
||
print(f.read())
|
||
f.close()
|
||
```
|
||
|
||
### Python 一次读取文件内容
|
||
|
||
在上面的例子中,您可以看到我们如何从文件中读取所有内容,以便打印它。如果您想一次将所有文件内容读入一个字符串,请对 file 对象使用`read()`方法,不带参数。这对于小文件来说没有问题,但是要意识到你是一次将所有的内容加载到内存中。这可能是大文件的一个问题,但是我们很快就会解决这个问题。
|
||
|
||
### 在 Python 中使用 with open()
|
||
|
||
在前面的例子中有一个潜在的问题。如果在 Python 读取文件时发生了[异常](https://python.land/deep-dives/python-try-except),那么`f.close()`调用将永远不会到达。这是一个微不足道的例子,但是你可以想象,对于更复杂的软件,其中的异常可能会被捕获,以便软件可以继续运行,它可能会很快出错。
|
||
|
||
另一个可能出现的问题是,您只是忘记编写 close()调用。它发生了,我们都是人类。因此,对于现代 Python,建议尽可能使用 with 语句。通过使用 Python 的`with open()`,打开的文件资源将只在缩进的代码块中可用:
|
||
|
||
[https://crumb.sh/embed/39hFGitfZhd](https://crumb.sh/embed/39hFGitfZhd)
|
||
|
||
如果上面的交互式示例由于某种原因无法运行,下面是相同代码的文本版本:
|
||
|
||
```py
|
||
with open('text.txt') as f:
|
||
text = f.read()
|
||
|
||
print(text)
|
||
```
|
||
|
||
在本例中,**with 语句确保文件关闭**,即使发生异常。这是因为一旦我们跳出这个 with 语句的范围,Python 就会自动关闭资源。由于这种自动关闭,您不能在 with 语句之外使用文件(在我们的例子中称为`f`)。如果你想再次使用它,你必须再次打开它。
|
||
|
||
### 文件模式
|
||
|
||
到目前为止,我们使用的`open()`函数只有一个参数:文件名。这是一个强制参数,但是 open()也有一些可选的额外参数,比如文件 ***模式*** 。该模式默认为“rt”,代表' **r** ead **t** ext '。你打开文件是为了读,这意味着你不能写,你希望它是一个文本文件。
|
||
|
||
下表列出了您可以通过的其他模式:
|
||
|
||
| 性格;角色;字母 | 意义 |
|
||
| **r** | 打开文件进行读取(默认) |
|
||
| **w** | 打开文件进行写入,首先截断文件 |
|
||
| **x** | 创建一个新文件并打开它进行写入 |
|
||
| **答** | 打开以供写入,如果文件已经存在,则追加到文件末尾 |
|
||
| **t** | 文本模式(默认),可与 rwxa 结合使用 |
|
||
| **b** | 二进制模式(与文本模式相反),可以与 rwxa 结合使用 |
|
||
| **+** | 打开磁盘文件进行更新(读写) |
|
||
|
||
可用于 open()的模式参数的文件模式
|
||
|
||
我不会在这里详细介绍所有的模式,而是在下面的适当部分解释和演示其中的大部分。
|
||
|
||
## Python 写文件
|
||
|
||
既然您已经了解了文件模式和如何打开文件,我们也可以使用 Python 来写入文件。我们可以分三步走:
|
||
|
||
1. 首先,我们需要确定文件模式。如果你看上面的表格,我们需要用 w 和 t。因为“t”是默认值,所以我们可以忽略它。
|
||
2. 接下来,我们需要打开文件进行写入
|
||
3. 最后,我们在文件对象上调用 write()方法。
|
||
|
||
在这种情况下,Write 需要一个参数:一个 [Python 字符串](https://python.land/introduction-to-python/strings)。如果您有另一种类型的数据,如数字,您需要手动将其转换为字符串。毕竟,文本文件没有数据类型的概念:它们只包含文本。
|
||
|
||
在下面的例子中,我们使用 str()函数将整数[转换为字符串,并将它们写入文件:](https://python.land/python-data-types/python-integer)
|
||
|
||
[https://crumb . sh/embed/3 nwtvkb 2 r xk](https://crumb.sh/embed/3NWTVKb2rXK)
|
||
|
||
用 Python 写一个文件,使用写模式
|
||
|
||
如果上面的交互示例不起作用,下面是相同的代码:
|
||
|
||
```py
|
||
with open('test.txt', 'w') as f:
|
||
for i in range(1, 5):
|
||
f.write(str(i))
|
||
|
||
with open('test.txt', 'r') as f:
|
||
print(f.read())
|
||
```
|
||
|
||
现在,您的文件系统中有一个文件(与您的脚本在同一个目录中),其内容如下:
|
||
|
||
```py
|
||
1234
|
||
```
|
||
|
||
### 换行符
|
||
|
||
你期望每个数字都在一个新的行上吗?
|
||
|
||
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these *free* articles, I hope you understand! **Support me by disabling your adblocker on my website** or, alternatively, **[buy me some coffee](https://www.buymeacoffee.com/pythonland)**. It's much appreciated and allows me to keep working on this site!
|
||
|
||
写文件时,需要显式指定换行符。你可以在上面的交互例子中尝试一下。修改代码,使其看起来像这样:
|
||
|
||
```py
|
||
with open('test.txt', 'w') as f:
|
||
for i in range(1, 5):
|
||
f.write(f'Number {i}\n')
|
||
|
||
with open('test.txt', 'r') as f:
|
||
print(f.read())
|
||
```
|
||
|
||
请注意,我在这里使用了 f 弦。我个人很喜欢它们,但是你也可以用类似`str(i) + '\n'`的东西。
|
||
|
||
## Python 追加到文件
|
||
|
||
现在我们有了一个测试文件,我们还可以添加一些额外的数据。默认情况下,当我们打开一个文件进行写入时,我们会覆盖任何已经存在的内容。因此,我们再次首先查看上面的文件模式表,我们可以使用哪种模式将数据追加到文件中。我们需要模式“a ”,用于追加。
|
||
|
||
在下面的例子中,我们:
|
||
|
||
1. 使用 Python 的`with open()`写入一个文件,就像前面的例子一样。
|
||
2. 之后,我们再次打开文件,这次使用 append 标志,并添加一些额外的行。
|
||
3. 回读文件内容并打印到屏幕上,以便我们可以检查结果
|
||
|
||
给你:
|
||
|
||
[https://crumb . sh/embed/4xb 5 eteh 6 FP](https://crumb.sh/embed/4xb5Eteh6fP)
|
||
|
||
用 Python 向文件追加文本
|
||
|
||
如果上面的交互示例不起作用,下面是相同的代码:
|
||
|
||
```py
|
||
# First create a file, with a couple of lines
|
||
with open('test.txt', 'w') as f:
|
||
for i in range(1, 5):
|
||
f.write(f'Number {i}\n')
|
||
|
||
# Now add some extra lines using append mode
|
||
with open('test.txt', 'a') as f:
|
||
for i in range(5, 8):
|
||
f.write(f'Append number {i}\n')
|
||
|
||
with open('test.txt') as f:
|
||
print(f.read())
|
||
```
|
||
|
||
## Python 读取文件到列表
|
||
|
||
对于小文件,将所有行一次读入一个列表会很方便。有两种方法可以做到这一点:
|
||
|
||
```py
|
||
with open('test.txt') as f:
|
||
lines = list(f)
|
||
|
||
# lines = ['1\n', '2\n', '3\n', '4\n', '5\n', '6\n', '7\n']
|
||
```
|
||
|
||
相当于:
|
||
|
||
```py
|
||
with open('test.txt') as f:
|
||
lines = f.readlines()
|
||
|
||
# lines = ['1\n', '2\n', '3\n', '4\n', '5\n', '6\n', '7\n']
|
||
```
|
||
|
||
### 逐行读取文件
|
||
|
||
你并不总是有一次阅读文件的奢侈。例如,当您需要处理 140 GB 大小的日志文件时,您不能将其全部读入内存并侥幸逃脱。你的程序可能会崩溃,或者至少变得非常慢。
|
||
|
||
在这种情况下,逐行解析文件要高效得多。为了逐行读取文件,我们可以将打开的文件视为一个[迭代器](https://python.land/deep-dives/python-iterator)。打开文件后,我们可以使用一个 [Python for-loop](https://python.land/introduction-to-python/python-for-loop) 来遍历这些行:
|
||
|
||
```py
|
||
with open('myfile.txt', 'r') as f:
|
||
for line in f:
|
||
print(line)
|
||
```
|
||
|
||
## 常见的 Python 文件操作
|
||
|
||
Python 有内置模块来执行常见的文件操作,比如删除文件、创建目录、移动文件等等。这些函数中的大部分都可以在`os`模块中获得,所以你需要先[导入它](https://python.land/project-structure/python-modules)。在这一节中,我们将介绍一些您可能需要执行的操作。
|
||
|
||
### 使用 Python 检查文件是否存在
|
||
|
||
要检查文件是否存在,我们可以使用`isfile`函数:
|
||
|
||
```py
|
||
import os
|
||
|
||
if os.path.isfile('myfile.txt'):
|
||
print("It's a file")
|
||
```
|
||
|
||
如果文件存在,这将返回`True`,如果不存在,则返回`False`。
|
||
|
||
### 使用 Python 检查目录是否存在
|
||
|
||
类似地,要检查目录是否存在,我们可以使用`isdir`函数:
|
||
|
||
```py
|
||
import os
|
||
|
||
if os.path.isdir('mydir'):
|
||
print("It's a directory")
|
||
```
|
||
|
||
如果目录存在,这将返回`True`。否则将返回`False`。
|
||
|
||
### 创建目录
|
||
|
||
要创建目录,使用`os`模块中的`mkdir`函数:
|
||
|
||
```py
|
||
import os
|
||
|
||
os.mkdir('mydir')
|
||
```
|
||
|
||
### 删除文件
|
||
|
||
要删除一个文件,我们可以使用下面的命令:
|
||
|
||
```py
|
||
import os
|
||
|
||
os.remove('myfile.txt')
|
||
```
|
||
|
||
这将删除文件。如果文件不存在,它将引发一个[异常](https://python.land/deep-dives/python-try-except)。
|
||
|
||
### 重命名(或移动)文件
|
||
|
||
要重命名或移动文件,我们可以使用以下命令:
|
||
|
||
```py
|
||
import os
|
||
os.rename('myfile.txt', 'myfile_renamed.txt')
|
||
```
|
||
|
||
只要文件在同一个文件系统中,重命名操作就会起作用。如果您想将一个文件从一个文件系统移动到另一个文件系统,您需要来自`shutil`的更高级的`move`函数,如下所述。
|
||
|
||
### 用 shutil 移动文件
|
||
|
||
为了以类似终端外壳上的`mv`命令的方式移动文件,我们可以使用`shutil`模块。与`os.rename`的一个重要区别是,这个函数也可以在文件系统之间移动文件。重命名只能重命名/移动同一文件系统中的文件:
|
||
|
||
```py
|
||
import shutil
|
||
|
||
shutil.move('/mnt/filesystem1/myfile.txt', '/mnt/filesystem2/mydir')
|
||
|
||
# Move to a directory, keeping the name intact
|
||
shutil.move('/home/erik/myfile.txt', '/home/erik/backups/')
|
||
```
|
||
|
||
### 用 Python 复制文件
|
||
|
||
`shutil`模块是一个更高级的模块,它提供了许多可用于复制单个文件或复制和删除整个文件树的功能:
|
||
|
||
```py
|
||
import shutil
|
||
|
||
# Copy a single file
|
||
shutil.copy('/home/erik/myfile.txt', '/home/erik/myfile_copy.txt')
|
||
|
||
# Copy entire tree of files
|
||
shutil.copytree('mydir', 'mydir_copy')
|
||
|
||
# Remove a tree of files
|
||
shutil.rmtree('mydir')
|
||
```
|
||
|
||
## 处理 Python 文件异常
|
||
|
||
如果出现错误,`shutil`和`os`模块将引发异常。您可以使用`try`和`except`模块来处理异常。您需要处理的异常几乎总是属于`OSError`类型。然而,有时您可能还需要处理其他异常,比如`SameFileError`。
|
||
|
||
我写了一个详细的教程,关于如何处理[异常和 try-catch 块](https://python.land/deep-dives/python-try-except),你可能想读一下。
|
||
|
||
## 关于 Unix 文件权限
|
||
|
||
在处理文件时,您不可避免地会遇到文件权限问题。这是关于 Unix 文件权限的简短入门。你迟早会遇到它们,尤其是在云中工作时,因为大多数云服务器都运行 Linux。
|
||
|
||
### 文件权限
|
||
|
||
权限是按文件指定的。当您列出目录时,您将能够看到其权限。例如,在 Linux 下,我们可以用 **`ls -l`** 命令这样做:
|
||
|
||
```py
|
||
$ ls -l
|
||
total 0
|
||
-rwxr-xr-x 1 erik erik 0 Jun 25 10:33 build_script.sh
|
||
-rw-r--r-- 1 erik erik 0 Jun 25 10:33 myapp.py
|
||
|
||
```
|
||
|
||
在输出中,您可以看到我出于演示目的创建的两个虚拟文件。第一个是脚本文件,第二个是名为 **myapp.py** 的 Python 文件。在这一行的开头,您会看到像 r、w 和 x 这样的神秘字母,它们定义了文件权限。文件可以具有以下权限:
|
||
|
||
| 信 | 许可 |
|
||
| **r** | 该文件可以被读取 |
|
||
| **w** | 文件是可写的 |
|
||
| **x** | 该文件是可执行的 |
|
||
| **–** | 未设置,未授予权限 |
|
||
|
||
可能的文件权限
|
||
|
||
### 用户和组
|
||
|
||
然而,仅仅知道权限是不够的。正如您在上面的示例清单中看到的,对于每个文件, **r** 重复了三次。如果你想知道为什么:计算机系统通常有多个用户和组。
|
||
|
||
每个文件都有三种访问类型的权限:
|
||
|
||
* 用户:文件的所有者。创建文件的人自动成为所有者
|
||
* 组:由 0 个或更多用户组成的组
|
||
* 其他所有人;也称为“世界”
|
||
|
||
权限也按此顺序列出。因此,对于每种访问类型,我们有三个权限字段,结果是一个九个字符长的字符串。下面的 ASCII 艺术应该可以澄清一些事情:
|
||
|
||
```py
|
||
User Group World
|
||
0 123 456 789
|
||
- rwx r-x r-x
|
||
|
||
Positions 1, 2, 3 define permissions for the user
|
||
Positions 4, 5, 6 define permissions for the group that the user is in
|
||
Positions 7, 8, 9 define permissions for world
|
||
Position 0 is the file type (see below)
|
||
```
|
||
|
||
### 文件类型
|
||
|
||
最后,有几种文件类型。最广为人知的是常规文件和目录,但还有更多类型。这是完整的列表:
|
||
|
||
* **–**:常规文件
|
||
* **d** :目录
|
||
* **c** :字符设备文件
|
||
* **b** :块设备文件
|
||
* **s** :本地套接字文件
|
||
* **p** :命名管道
|
||
* **l** :符号链接
|
||
|
||
为了演示,您可以检查文件`/dev/random`:
|
||
|
||
```py
|
||
ls -l /dev/random
|
||
crw-rw-rw- 1 root root 1, 8 jun 22 08:44 /dev/random
|
||
|
||
```
|
||
|
||
这是一个字符设备,当读取时提供一个随机数据流。如您所见,它也是可写的。在这种情况下,写入`/dev/random`将更新熵池。
|
||
|
||
还有更多的东西需要学习,您可能也想深入了解我们关于使用 Unix shell 的章节。所以,如果你想或需要,就去探索吧,但是这里的快速介绍应该可以让你开始。
|
||
|
||
## 继续学习
|
||
|
||
使用这些资源扩展您的知识和理解:
|
||
|
||
* 如何在 Python 中加载、读取和写入 [YAML](https://python.land/data-processing/python-yaml)
|
||
* 在 Python 中使用[JSON](https://python.land/data-processing/working-with-json)
|
||
* 关于使用文件的官方 Python 文档
|
||
* [官方](https://docs.python.org/3/library/shutil.html) shutil 文档
|
||
* 维基百科关于[文件权限的页面](https://en.wikipedia.org/wiki/File-system_permissions)
|
||
* 我们关于使用 unix shell 的章节 |