Python文件,目录与eval函数

2024-11-07 15:39

文件的基本操作

操作文件的步骤

在计算机中要操作文件的套路非常固定,一共包含三个步骤:

  1. 打开文件;

  2. 读、写文件:是指将文件内容读入内存;将内存内容写入文件;

  3. 关闭文件。

操作文件的函数/方法

在Python中要担任文件需要记住1个函数和3个方法,如下所示:

序号

函数/方法

说明

01

open

打开文件,并返回文件操作对象

02

read

将文件内容读取到内存

03

write

将指定内容写入文件

04

close

关闭文件

其中open函数负责打开文件,并返回文件对象,而read/write/close这三个方法都需要通过文件对象来调用。

read方法——读取文件

  1. open函数的第一个参数是要打开的文件(文件名是区分大小写的),如果文件存在,返回文件操作对象,如果文件不存在,会抛出异常

  2. read文件可以一次性读入并返回文件的所有内容。

  3. close方法负责关闭文件。如果忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问。因此,当我们open()文件后,就在最后面输入close(),对应起来,再写中间部分,这样会避免忘记关闭文件。

需要注意的是,方法执行后,会把文件指针移动到文件的末尾

现在来看一个案例,我们新建一个README.txt文件,如下所示:

hello python!
hello world!

在同一个目录下新建一个python文件,命名为hm_01_读取文件.py,如下所示:

# 1. 打开文件
file = open("README")

# 2. 读取文件内容
text = file.read()
print(text)

# 3. 关闭文件
file.close()

运行结果如下所示:

hello python!
hello world!

文件指针

文件指针会标记从哪个位置开始读取数据,第一次打开文件时,通常文件指针会指向文件的开始位置,如下所示:

img

当执行了read方法后,文件指标会移动到读取内容的末尾,默认情况下会移动到文件末尾,如下所示:

img

因此,当我们执行了一次read方法后,读取了所有内容,那么再次调用read方法,就无法获取内容,因此此时文件指标已经移到了文件的末尾。现在我们来验证一下,还是改造原来的代码:

# 1. 打开文件
file = open("README.txt")

# 2. 读取文件内容
text = file.read()
print(text)

print("-" * 50)

text = file.read()
print(text)

# 3. 关闭文件
file.close()

运行结果如下所示:

Hello, python!
Hello, world!
--------------------------------------------------

从结果中我们可以发现,第二次调用read方法后,没有内容读取。我们还可以看一下读取文件内容的长度,如下所示:

# 1. 打开文件
file = open("README.txt")

# 2. 读取文件内容
text = file.read()
print(text)
print(len(text)) #输出读取文件长度

print("-" * 50)

text = file.read()
print(text)
print(len(text))

# 3. 关闭文件
file.close()

运行结果如下所示:

Hello, python!
Hello, world!
29
--------------------------------------------------

0

从结果也可以发现,第二次读取的长度是0。

打开文件的方式

open在默认的情况下是以只读的方式打开文件,并且返回文件对象,语法如下所示:

f = open("文件名", "访问方式")

模式

描述

r

以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。

rb

以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。

r+

打开一个文件用于读写。文件指针将会放在文件的开头。

rb+

以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。

w

打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

wb

以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

w+

打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

wb+

以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

a

打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

ab

以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

a+

打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。

ab+

以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

写入文件

现在我们向README.txt文件中写入HELLO,如下所示:

# 1. 打开文件
file = open("README.txt", "w")

# 2. 写入文件
file.write("HELLO")

# 3. 关闭文件
file.close()

查看一下README.txt文件,我们使用了w参数来写入文件,如果原文件中有内容,那么就会覆盖,如果没有这个文件,就新建。

现在我们来看一下参数a,这个参数的功能在于以追加的方式写入文件,如下所示:

# 1. 打开文件
file = open("README.txt", "a")

# 2. 写入文件
file.write("123HELLO")

# 3. 关闭文件
file.close()

现在我们打开文件README.txt,结果为HELLO123HELLO。如果分别在参数r,w,a的后面添加上+号,即r+, w+, a+,那么就会以读写的方式打开文件,这会造成频繁地移动文件指标,影响文件的读写效率,在开发中,更多的是采用只读只写的方式来操作文件。

按行读取文件内容

read方法会默认把文件的所有内容一次性读取到内存,如果文件太大,对内存的占用会非常严重。此时我们可以使用readline方法。

readline方法

readline方法可以一次读取一行内容,方法执行后,会把文件指针移动到下一行,准备再次读取。因此我们在读取大文件时的代码通常是如下所示:

file = open("README.txt")

while True:
    text = file.readline()

    # 判断是否读取到内容
    if not text:
        break

    print(text)

file.close()

结果运行如下所示:

Hello1

Hello2

Hello3

文件读写案例——复制文件

在这个案例中,使用代码的方式来实现文件的复制过程。如果我们要复制的源文件是一个小文件,那么我们就可以使用read方法直接把文件的内容全部读取下来,再写入到另外一个文件中,如下所示:

# 1. 打开文件
file_read = open("README.txt")
file_write = open("README[复制]", "w")

# 2. 读、写
text = file_read.read()
file_write.write(text)

# 3. 关闭文件
file_read.close()
file_write.close()

现在在同一个目录下出现了README[复制].txt这个文件,里面的内容与README.txt完全相同。

再看一个案例,这个是案例是复制大文件。

如果要复制大文件,就不能使用read方法,因为这会对内存造成很大的压力,因此可以使用readline方法。

现在我们在源文件README.txt中输入以下内容:

Hello1
Hello2
Hello345

运行复制大文件的代码,如下所示:

# 1. 打开文件
file_read = open("README.txt")
file_write = open("README[复制]", "w")

# 2. 读、写
while True:
    # 读取一行内容
    text = file_read.readline()

    # 判断是否读取到内容
    if not text:
        break

    file_write.write(text)

# 3. 关闭文件
file_read.close()
file_write.close()

运行后,打开复制好的文件,README[复制],结果与源文件一样。

文件/目录的常用管理操作

终端/浏览器中可以执行常规的文件/目录操作,例如创建、重命名、删除、改变路径、查看目录内容等,在python中,如果要实现上述功能,通常使用os模块。

Python中的目录操作有这些:

序号

方法及描述

1

os.access(path, mode):检验权限模式

2

os.chdir(path):改变当前工作目录

3

os.chflags(path, flags):设置路径的标记为数字标记。

4

os.chmod(path, mode):更改权限

5

os.chown(path, uid, gid):更改文件所有者

6

os.chroot(path):改变当前进程的根目录

7

os.close(fd):关闭文件描述符 fd

8

os.closerange(fd_low, fd_high):关闭所有文件描述符,从 fd_low (包含) 到 fd_high (不包含), 错误会忽略

9

os.dup(fd):复制文件描述符 fd

10

os.dup2(fd, fd2):将一个文件描述符 fd 复制到另一个 fd2

11

os.fchdir(fd):通过文件描述符改变当前工作目录

12

os.fchmod(fd, mode):改变一个文件的访问权限,该文件由参数fd指定,参数mode是Unix下的文件访问权限。

13

os.fchown(fd, uid, gid):修改一个文件的所有权,这个函数修改一个文件的用户ID和用户组ID,该文件由文件描述符fd指定。

14

os.fdatasync(fd):强制将文件写入磁盘,该文件由文件描述符fd指定,但是不强制更新文件的状态信息。

15

os.fdopen(fd[, mode[, bufsize]]):通过文件描述符 fd 创建一个文件对象,并返回这个文件对象

16

os.fpathconf(fd, name):返回一个打开的文件的系统配置信息。name为检索的系统配置的值,它也许是一个定义系统值的字符串,这些名字在很多标准中指定(POSIX.1, Unix 95, Unix 98, 和其它)。

17

os.fstat(fd):返回文件描述符fd的状态,像stat()。

18

os.fstatvfs(fd):返回包含文件描述符fd的文件的文件系统的信息,像 statvfs()

19

os.fsync(fd):强制将文件描述符为fd的文件写入硬盘。

20

os.ftruncate(fd, length):裁剪文件描述符fd对应的文件, 所以它最大不能超过文件大小。

21

os.getcwd():返回当前工作目录

22

os.getcwdu():返回一个当前工作目录的Unicode对象

23

os.isatty(fd):如果文件描述符fd是打开的,同时与tty(-like)设备相连,则返回true, 否则False。

24

os.lchflags(path, flags):设置路径的标记为数字标记,类似 chflags(),但是没有软链接

25

os.lchmod(path, mode):修改连接文件权限

26

os.lchown(path, uid, gid):更改文件所有者,类似 chown,但是不追踪链接。

27

os.link(src, dst):创建硬链接,名为参数 dst,指向参数 src

28

os.listdir(path):返回path指定的文件夹包含的文件或文件夹的名字的列表。

29

os.lseek(fd, pos, how):设置文件描述符 fd当前位置为pos, how方式修改: SEEK_SET 或者 0 设置从文件开始的计算的pos; SEEK_CUR或者 1 则从当前位置计算; os.SEEK_END或者2则从文件尾部开始. 在unix,Windows中有效

30

os.lstat(path):像stat(),但是没有软链接

31

os.major(device):从原始的设备号中提取设备major号码 (使用stat中的st_dev或者st_rdev field)。

32

os.makedev(major, minor):以major和minor设备号组成一个原始设备号

33

os.makedirs(path[, mode]):递归文件夹创建函数。像mkdir(), 但创建的所有intermediate-level文件夹需要包含子文件夹。

34

os.minor(device):从原始的设备号中提取设备minor号码 (使用stat中的st_dev或者st_rdev field )。

35

os.mkdir(path[, mode]):以数字mode的mode创建一个名为path的文件夹.默认的 mode 是 0777 (八进制)。

36

os.mkfifo(path[, mode]):创建命名管道,mode 为数字,默认为 0666 (八进制)

37

os.mknod(filename[, mode=0600, device]):创建一个名为filename文件系统节点(文件,设备特别文件或者命名pipe)。

38

os.open(file, flags[, mode]):打开一个文件,并且设置需要的打开选项,mode参数是可选的

39

os.openpty():打开一个新的伪终端对。返回 pty 和 tty的文件描述符。

40

os.pathconf(path, name):返回相关文件的系统配置信息。

41

os.pipe():创建一个管道. 返回一对文件描述符(r, w) 分别为读和写

42

os.popen(command[, mode[, bufsize]]):从一个 command 打开一个管道

43

os.read(fd, n):从文件描述符 fd 中读取最多 n 个字节,返回包含读取字节的字符串,文件描述符 fd对应文件已达到结尾, 返回一个空字符串。

44

os.readlink(path):返回软链接所指向的文件

45

os.remove(path):删除路径为path的文件。如果path 是一个文件夹,将抛出OSError; 查看下面的rmdir()删除一个 directory。

46

os.removedirs(path):递归删除目录。

47

os.rename(src, dst):重命名文件或目录,从 src 到 dst

48

os.renames(old, new):递归地对目录进行更名,也可以对文件进行更名。

49

os.rmdir(path):删除path指定的空目录,如果目录非空,则抛出一个OSError异常。

50

os.stat(path):获取path指定的路径的信息,功能等同于C API中的stat()系统调用。

51

os.stat_float_times([newvalue]):决定stat_result是否以float对象显示时间戳

52

os.statvfs(path):获取指定路径的文件系统统计信息

53

os.symlink(src, dst):创建一个软链接

54

os.tcgetpgrp(fd):返回与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组

55

os.tcsetpgrp(fd, pg):设置与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组为pg。

56

os.tempnam([dir[, prefix]]):Python3 中已删除。返回唯一的路径名用于创建临时文件。

57

os.tmpfile():Python3 中已删除。返回一个打开的模式为(w+b)的文件对象 .这文件对象没有文件夹入口,没有文件描述符,将会自动删除。

58

os.tmpnam():Python3 中已删除。为创建一个临时文件返回一个唯一的路径

59

os.ttyname(fd):返回一个字符串,它表示与文件描述符fd 关联的终端设备。如果fd 没有与终端设备关联,则引发一个异常。

60

os.unlink(path):删除文件路径

61

os.utime(path, times):返回指定的path文件的访问和修改的时间。

62

os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]]):输出在文件夹中的文件名通过在树中游走,向上或者向下。

63

os.write(fd, str):写入字符串到文件描述符 fd中. 返回实际写入的字符串长度

目录操作的函数太多,用到的时候再学习,下面只列出几个简单的案例:

显示某文件夹下的所有文件名

代码如下:

C:\Users\20161111>type practice.py
import os
for filename in os.listdir('d:/Software'):
    print(filename)
    
C:\Users\20161111>python practice.py
office_tools
Professional_tools
ProgramTool
SnapGene 3.2.1 Win
system_enhance
windows_iso
谷歌批量翻译

创建某个目录

>>> import os
>>> os.mkdir("d:/Software/test")
>>> exit()
C:\Users\20161111>d:
D:\>cd software
D:\software>dir
 Volume in drive D is 新加卷
 Volume Serial Number is C0C6-2E4F

 Directory of D:\software

2018/05/24  09:33    <DIR>          .
2018/05/24  09:33    <DIR>          ..
2018/05/08  13:46    <DIR>          office_tools
2018/05/21  10:30    <DIR>          Professional_tools
2018/05/08  13:46    <DIR>          ProgramTool
2017/11/22  20:21    <DIR>          SnapGene 3.2.1 Win
2018/05/13  21:38    <DIR>          system_enhance
2018/05/24  09:33    <DIR>          test # 刚刚创建的文件夹
2018/05/11  00:48    <DIR>          windows_iso
2017/11/02  10:52    <DIR>          谷歌批量翻译
               1 File(s)              0 bytes
              10 Dir(s)  49,307,074,560 bytes free

删除某个文件夹

命令rmdir,如下所示:

>>> import os
>>> os.rmdir('d:/Software/test')
>>> exit()

D:\software>dir
 Volume in drive D is 新加卷
 Volume Serial Number is C0C6-2E4F

 Directory of D:\software

2018/05/25  12:40    <DIR>          .
2018/05/25  12:40    <DIR>          ..
2018/05/08  13:46    <DIR>          office_tools
2018/05/21  10:30    <DIR>          Professional_tools
2018/05/08  13:46    <DIR>          ProgramTool
2017/11/22  20:21    <DIR>          SnapGene 3.2.1 Win
2018/05/13  21:38    <DIR>          system_enhance
2018/05/11  00:48    <DIR>          windows_iso
2017/11/02  10:52    <DIR>          谷歌批量翻译
               1 File(s)              0 bytes
               9 Dir(s)  48,131,309,568 bytes free

D:\Software\test这个文件夹删除了 。

重命名某个文件

如下所示:

D:\netdisk\bioinfo.notes\Python\黑马教程笔记\13_文件>ipython
Python 3.6.3 |Anaconda, Inc.| (default, Oct 15 2017, 03:27:45) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import os

In [2]: os.rename("README.txt","TEST.txt")

In [3]: ls
 Volume in drive D is 新加卷
 Volume Serial Number is C0C6-2E4F

 Directory of D:\netdisk\bioinfo.notes\Python\黑马教程笔记\13_文件

2019/06/09  16:51    <DIR>          .
2019/06/09  16:51    <DIR>          ..
2019/06/09  16:46    <DIR>          .idea
2019/06/09  15:39               164 hm_01_读取文件.py
2019/06/09  16:19               229 hm_02_读取文件后文件指针会改变.py
2019/06/09  16:25               129 hm_03_写入文件
2019/06/09  16:37               177 hm_04_分行读取文件.py
2019/06/09  16:41               224 hm_05_复制文件.py
2019/06/09  16:44               347 hm_06_复制大文件.py
2019/06/09  16:46                24 README[复制]
2019/06/09  16:45                24 TEST.txt
               8 File(s)          1,318 bytes
               3 Dir(s)  52,836,925,440 bytes free

显示当前目录内的所有文件

使用os模块下的listdir命令,如下所示:

In [4]: os.listdir(".")
Out[4]:
['.idea',
 'hm_01_读取文件.py',
 'hm_02_读取文件后文件指针会改变.py',
 'hm_03_写入文件',
 'hm_04_分行读取文件.py',
 'hm_05_复制文件.py',
 'hm_06_复制大文件.py',
 'README[复制]',
 'TEST.txt']

判断文件是目录还是文件

使用os模块中的path.isdir命令,如下所示:

In [5]: os.path.isdir("TEST.txt")
Out[5]: False

In [6]: os.path.isdir(".idea")
Out[6]: True

eval函数

eval()函数可以接受一个字符串,将其当成有效的表达式来求值,并返回计算结果,来看一下面代码的运行结果:

In [1]: # 基本的数学计算

In [2]: eval("1 + 1")
Out[2]: 2

In [3]: # 字符串重复

In [4]: eval("'*' * 10")
Out[4]: '**********'

In [5]: # 将字符串转换成列表

In [6]: type(eval("[1, 2, 3, 4, 5]"))
Out[6]: list

In [7]: # 将字符串转换成字典

In [8]: type(eval("{'name':'xiaoming', 'age':18}"))
Out[8]: dict

eval案例——计算器

需求如下:

  1. 提示用户输入一个加减乘除混合运行;

  2. 返回过计算结果。

代码如下所示:

input_str = input("请输入算术题:  ")

print(eval(input_str))

运行结果如下所示:

请输入算术题:  (5+4)*5
45

不要滥用eval

在开发过程中,不要使用eval来直接转换input结果,现在我们来看一下为什么不要这么做。

我们先来看一下这行代码(由于本人是在windows环境下运行的,因此使用的是dir命令,它等于同Linux环境下的ls命令,这一点与视频中的不一样):

__import__('os').system('dir')

上面的这行代码等于:

import os
os.system("终端命令")

如果执行成功,返回0,执行失败,返回错误信息。现在还看上面的计算器案例,运行后,如果我们输入的内容是__import__('os').system('dir'),我们看一下计算的结果:

C:\Anaconda3\python.exe D:/netdisk/bioinfo.notes/Python/黑马教程笔记/13_文件/hm_08_eval计算器.py
请输入算术题:  __import__('os').system('dir')
 驱动器 D 中的卷是 新加卷
 卷的序列号是 C0C6-2E4F

 D:\netdisk\bioinfo.notes\Python\黑马教程笔记\13_文件 的目录

2019/06/09  19:54    <DIR>          .
2019/06/09  19:54    <DIR>          ..
2019/06/09  19:57    <DIR>          .idea
2019/06/09  15:39               164 hm_01_读取文件.py
2019/06/09  16:19               229 hm_02_读取文件后文件指针会改变.py
2019/06/09  16:25               129 hm_03_写入文件
2019/06/09  16:37               177 hm_04_分行读取文件.py
2019/06/09  16:41               224 hm_05_复制文件.py
2019/06/09  16:44               347 hm_06_复制大文件.py
2019/06/09  19:54                68 hm_08_eval计算器.py
2019/06/09  16:46                24 README[复制]
2019/06/09  16:45                24 TEST.txt
               9 个文件          1,386 字节
               3 个目录 52,828,184,576 可用字节

现在我们再换一个命令输入,输入__import__('os').system('cd.>a.txt'),结果就会在同一个目录下创建了一个名为a.txt的文本文件。

现在我们再换一个命令,输入__import__('os').system('del a.txt'),此时就会把刚刚创建的那个a.txt文件删除。

此时我们应该就明白了,使用eval()函数直接转换input的结果了,如果用户使用这个函数直接调用os模块中的命令,就会执行任何终端命令,一旦操作出现失误,会造成严重的后果。

相关文章
热点文章
精彩视频
Tags

站点地图 在线访客: 今日访问量: 昨日访问量: 总访问量: