py6 文件处理
文件处理
一、文件操作
打开文件时,需要指定文件路径和以何等方式打开文件,打开后,即可获取该文件句柄,日后通过此文件句柄对该文件操作。
打开文件的模式有:
- r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】(不加mode时默认为r模式)
- w,只写模式【不可读;不存在则创建;存在则清空内容】(相当于重新创建了文件)
- x, 只写模式【不可读;不存在则创建,存在则报错】
- a, 追加模式【不可读; 不存在则创建;存在则只追加内容】
"+" 表示可以同时读写某个文件
- r+, 读写【可读,可写】
- w+,写读【可读,可写】
- x+ ,写读【可读,可写】
- a+, 写读【可读,可写】
"U"表示在读取时,可以将 \r \n \r\n自动转换成 \n (与 r 或 r+ 模式同使用)
- rU
- r+U
"b"表示以字节的方式操作
- rb 或 r+b
- wb 或 w+b
- xb 或 w+b
- ab 或 a+b
注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码
文件操作各种方法详解
方法 | 描述 |
f.close() | 关闭文件,记住用open()打开文件后一定要记得关闭它,否则会占用系统的可打开文件句柄数。 |
f.fileno() | 获得文件描述符,是一个数字 |
f.flush() | 刷新输出缓存 |
f.isatty() | 如果文件是一个交互终端,则返回True,否则返回False。 |
f.read([count]) | 默认读出全部文件,如果有count,则读出count个字符。 |
f.readline() | 读出一行信息。 |
f.readlines() | 读出所有行,也就是读出整个文件的信息。 |
f.seek(offset[,where]) | 把文件指针移动到相对于where的offset位置。where为0表示文件开始处,这是默认值 ;1表示当前位置;2表示文件结尾。 |
f.seekable() | 判断光标是否可移动,像tty文件或终端设备文件光标不可移动 |
f.tell() | 获得文件指针位置。 |
f.truncate([size]) | 截取文件,使文件的大小为size。 |
f.write(string) | 把string字符串写入文件。 |
f.writelines(list) | 把list中的字符串一行一行地写入文件,是连续写入文件,没有换行。 |
示例文件
Somehow, it seems the love I knew was always the most destructive kind 不知为何,我经历的爱情总是最具毁灭性的的那种 Yesterday when I was young 昨日当我年少轻狂 The taste of life was sweet 生命的滋味是甜的 As rain upon my tongue 就如舌尖上的雨露 I teased at life as if it were a foolish game 我戏弄生命 视其为愚蠢的游戏 The way the evening breeze 就如夜晚的微风 May tease the candle flame 逗弄蜡烛的火苗 The thousand dreams I dreamed 我曾千万次梦见 The splendid things I planned 那些我计划的绚丽蓝图 I always built to last on weak and shifting sand 但我总是将之建筑在易逝的流沙上 I lived by night and shunned the naked light of day 我夜夜笙歌 逃避白昼赤裸的阳光 And only now I see how the time ran away 事到如今我才看清岁月是如何匆匆流逝 Yesterday when I was young 昨日当我年少轻狂 So many lovely songs were waiting to be sung 有那么多甜美的曲儿等我歌唱 So many wild pleasures lay in store for me 有那么多肆意的快乐等我享受 And so much pain my eyes refused to see 还有那么多痛苦 我的双眼却视而不见 I ran so fast that time and youth at last ran out 我飞快地奔走 最终时光与青春消逝殆尽 I never stopped to think what life was all about 我从未停下脚步去思考生命的意义 And every conversation that I can now recall 如今回想起的所有对话 Concerned itself with me and nothing else at all 除了和我相关的 什么都记不得了 The game of love I played with arrogance and pride 我用自负和傲慢玩着爱情的游戏 And every flame I lit too quickly, quickly died 所有我点燃的火焰都熄灭得太快 The friends I made all somehow seemed to slip away 所有我交的朋友似乎都不知不觉地离开了 And only now I'm left alone to end the play, yeah 只剩我一个人在台上来结束这场闹剧 Oh, yesterday when I was young 噢 昨日当我年少轻狂 So many, many songs were waiting to be sung 有那么那么多甜美的曲儿等我歌唱 So many wild pleasures lay in store for me 有那么多肆意的快乐等我享受 And so much pain my eyes refused to see 还有那么多痛苦 我的双眼却视而不见 There are so many songs in me that won't be sung 我有太多歌曲永远不会被唱起 I feel the bitter taste of tears upon my tongue 我尝到了舌尖泪水的苦涩滋味 The time has come for me to pay for yesterday 终于到了付出代价的时间 为了昨日 When I was young 当我年少轻狂
read():读取文件全部
打开文件并全部读出(read()方法)
f = open("demo", 'r', encoding="utf-8") print(f.read())
write():写入文件(写入时指定编码,wb形式无法指定)
f = open("demo", 'w', encoding="utf-8") f.write('看看效果了\n') f.write('换行了,不加反斜杠n不换行')
readline():读取文件一行
f = open("demo", 'w', encoding="utf-8") print(f.readline().strip()) print(f.readline().strip()) print(f.readline().strip()) print(f.readline().strip())
readlines():将文件所有按行划分成列表元素并装入列表
f = open('demo', 'r', encoding='utf-8') print(f.readlines()) # ['Somehow, it seems the love I knew was always the most destructive kind\n', '不知为何,我经历的爱情总是最具毁灭性的的那种\n',............'终于到了付出代价的时间 为了昨日\n', 'When I was young\n', '当我年少轻狂'] for line in f.readlines(): # 循环打印所有 print(line.strip()) # 跳过第九行打印 for index, line in enumerate(f.readlines()): if index == 9: print("==========================") continue print(line.strip())
注:循环读出文件建议如下
for line in f: # 这样机器内存里值保存一行,最高效
print(line)
tell():返回文件内光标所在位置
f = open('demo', 'r', encoding='utf-8') f.readline() f.readline() print(f.tell()) # 140
seek():将文件内光标移到指定位置
read()和write()都是在光标后读写,r+模式的默认例外
f.readline() f.readline() f.seek(2) print(f.tell()) # 2
flush():将文件实时刷新到硬盘上(python3.x)
文件运行时是与缓存交换信息,等缓存满了,缓存一次性刷到硬盘,硬盘(存二进制,插入需后面全往后移,不现实)改东西只能覆盖,不能插入文件打开模式分析
‘r’模式:只读模式
只能读取文件,是默认模式,文件必须存在,不存在则抛出异常
read()和write()都是在光标后读写,r+模式的默认例外
‘w’模式:只写模式
用了只写模式相当于重新创建问价,不可读;不存在则创建;存在则清空内容重写
’a’模式:追加模式
可读;不存在则创建;存在则只追加内容。无论光标在哪,是否使用seek,追加模式只能在文件的末尾进行写(追加)操作(a,a+)
’r+’模式:读写模式
可读可写,在不手动用seek()移动光标时,write()默认会写到文件的最后,其余模式都是在光标后操作,因为在文件内写是覆盖,并不是插入,为了保护文件
但是若自己手动seek()移动了光标,则可在seek()处写(其实是覆盖)(对比a,a+)
但是覆盖也有问题(可能会有编码错误)
‘w+’模式:写读模式
可读可写,但是因为是写读模式,类似‘w’模式,相当于重新创建文件,所以只能先写在读
‘rb’模式:二进制读模式
二进制的方式读取,不需要加编码方式
f
=
open
(
'dingxi'
,
'rb'
)
文件修改
f=open('dingxi','r',encoding='utf8') new_f=open('dingxi2','w',encoding='utf8') for line in f: if '多想和你一样臭不要脸' in line: line = line.replace('多想和你一样臭不要脸','就是这么的臭不要脸') new_f.write(line) f.close() new_f.close() # 执行结果:创建一个新的文件,并且替换原来的‘多想和你一样臭不要脸’
with语句
为了避免打开文件后忘记关闭,可以通过with管理上下文,当with代码块执行完毕时,内部会自动关闭并释放文件资源。with语句的基本语法结构如下:
with expression [as variable]: with-block
在Python 2.7 后,with又支持同时对多个文件的上下文进行管理,即:
with open('dingxi','r',encoding='utf8') as f,open('Lry','r',encoding='utf8') as f1: print(f.readline()) print(f1.readline())
是不是发现使用with语句相对try/finally来说简洁了很多,而且也不需要每一个用户都去写f.close()来关闭文件了,这是因为with语句在背后做了大量的工作。with语句的expression是上下文管理器,这个我们下文会说。with语句中的[as variable]是可选的,如果指定了as variable说明符,则variable是上下文管理器expression调用__enter__()函数返回的对象。所以,f并不一定就是expression,而是expression.__enter__()的返回值,至于expression.__enter__()返回什么就由这个函数来决定了。with-block是执行语句,with-block执行完毕时,with语句会自动进行资源清理,对应上面例子就是with语句会自动关闭文件。
下面我们来具体说下with语句在背后默默无闻地到底做了哪些事情。刚才我们说了expression是一个上下文管理器,其实现了__enter__和__exit__两个函数。当我们调用一个with语句时,执行过程如下:
1.首先生成一个上下文管理器expression,在上面例子中with语句首先以“test.txt”作为参数生成一个上下文管理器open("test.txt")。
2.然后执行expression.__enter__()。如果指定了[as variable]说明符,将__enter__()的返回值赋给variable。上例中open("test.txt").__enter__()返回的是一个文件对象给f。
3.执行with-block语句块。上例中执行读取文件。
4.执行expression.__exit__(),在__exit__()函数中可以进行资源清理工作。上面例子中就是执行文件的关闭操作。
with语句不仅可以管理文件,还可以管理锁、连接等等,如下面的例子:
#管理锁 import threading lock = threading.lock() with lock: #执行一些操作 pass
下文管理器
在上文中我们提到with语句中的上下文管理器。with语句可以如此简单但强大,主要依赖于上下文管理器。那么什么是上下文管理器?上下文管理器就是实现了上下文协议的类,而上下文协议就是一个类要实现__enter__()和__exit__()两个方法。一个类只要实现了__enter__()和__exit__(),我们就称之为上下文管理器下面我们具体说下这两个方法。
__enter__():主要执行一些环境准备工作,同时返回一资源对象。如果上下文管理器open("test.txt")的__enter__()函数返回一个文件对象。
__exit__():完整形式为__exit__(type, value, traceback),这三个参数和调用sys.exec_info()函数返回值是一样的,分别为异常类型、异常信息和堆栈。如果执行体语句没有引发异常,则这三个参数均被设为None。否则,它们将包含上下文的异常信息。__exit_()方法返回True或False,分别指示被引发的异常有没有被处理,如果返回False,引发的异常将会被传递出上下文。如果__exit__()函数内部引发了异常,则会覆盖掉执行体的中引发的异常。处理异常时,不需要重新抛出异常,只需要返回False,with语句会检测__exit__()返回False来处理异常。
如果我们要自定义一个上下文管理器,只需要定义一个类并且是实现__enter__()和__exit__()即可。下面通过一个简单的例子是演示如果新建自定义的上下文管理器,我们以数据库的连接为例。在使用数据库时,有时要涉及到事务操作。数据库的事务操作当调用commit()执行sql命令时,如果在这个过程中执行失败,则需要执行rollback()回滚数据库,通常实现方式可能如下:
def test_write(): con = MySQLdb.connection() cursor = con.cursor() sql = """ #具体的sql语句 """ try: cursor.execute(sql) cursor.execute(sql) cursor.execute(sql) con.commit() #提交事务 except Exception as ex: con.rollback() #事务执行失败,回滚数据库
如果想通过with语句来实现数据库执行失败的回滚操作,则我们需要自定义一个数据库连接的上下文管理器,假设为DBConnection,则我们将上面例子用with语句来实现的话,应该是这样子的,如下:
def test_write(): sql = """ #具体的sql语句 """ con = DBConnection() with con as cursor: cursor.execute(sql) cursor.execute(sql) cursor.execute(sql)
要实现上面with语句的功能,则我们的DBConnection数据库上下文管理器则需要提供一下功能:__enter__()要返回一个连接的cursor; 当没有异常发生是,__exit__()函数commit所有的数据库操作。如果有异常发生则_exit__()会回滚数据库,调用rollback()。所以我们可以实现DBConnection如下:
def DBConnection(object): def __init__(self): pass def cursor(self): #返回一个游标并且启动一个事务 pass def commit(self): #提交当前事务 pass def rollback(self): #回滚当前事务 pass def __enter__(self): #返回一个cursor cursor = self.cursor() return cursor def __exit__(self, type, value, tb): if tb is None: #没有异常则提交事务 self.commit() else: #有异常则回滚数据库 self.rollback()
Python 文件夹及文件操作
我们经常会与文件和目录打交道,对于这些操作,python可以使用 os 及 shutill 模块,其中包含了很多操作文件和目录的函数。
os 可以执行简单的文件夹及文件操作,引入用 import os,可用 help(os) 或是 dir(os) 查看其用法。注意有些函数在os模块中,有的是在os.path模块中。
shutil 模块提供了大量的文件的高级操作,特别针对文件拷贝和删除。主要功能为目录和文件操作以及压缩操作。须引入 import shutil ,具体 help。本文仅介绍移动、复制及删除。
可先在 D:\ 下创建文件夹 Python_os , 再在其下创建文件夹 os, 再在其下创建 test.txt;之后的示例会在该文件夹下操作
判断路径或文件
os.path.isabs(...) # 判断是否绝对路径
os.path.exists(...) # 判断是否真实存在
os.path.isdir(...) # 判断是否是个目录
os.path.isfile(...) # 判断是否是个文件
注意: 把两个路径合成一个时,不要直接拼字符串,而要通过 os.path.join(part1,part2)
函数,这样可以正确处理不同操作系统的路径分隔符。在Linux/Unix/Mac下,os.path.join()
返回这样的字符串: part1/part2
而Windows下会返回这样的字符串: part1\part2
1 import os 2 import shutil 3 4 file_dir = "D:\\Python_os\\os" # 注意 \\ ;windows 下是这么表示的;Linux 和 Mac 是 / 5 file_name = "test.txt" 6 file_abs = os.path.join(file_dir, file_name) # os.path.join(...) 表示路径链接 7 8 9 '''判断路径或文件''' 10 print (1,os.path.isabs(file_dir)) # 判断是否绝对路径 11 print (2,os.path.isabs(file_name)) 12 print (3,os.path.isabs(file_abs)) 13 print (4,os.path.exists(file_abs)) # 判断是否真实存在 14 print (5,os.path.exists(os.path.join(file_dir,"xxx"))) 15 print (6,os.path.isdir(file_dir)) # 判断是否是个目录 16 print (7,os.path.isdir(file_abs)) 17 print (8,os.path.isfile(file_dir)) # 判断是否是个文件 18 print (9,os.path.isfile(file_abs))
运行结果:
路径名、文件名分隔
os.path.split(...) # 分隔目录和文件名/文件夹名
os.path.splitdrive(...) # 分隔盘符(windows系统)
os.path.splitext(...) # 分隔文件和扩展名
运行结果:
这些合并、拆分路径的函数并不要求目录和文件要真实存在,它们只对字符串进行操作。
工作目录及创建文件夹操作
os.getcwd() # 获取当前工作目录
os.chdir(...) # 改变工作目录
os.listdir(...) # 列出目录下的文件
os.mkdir(...) # 创建单个目录 注意:创建多级用 os.makedirs()
os.makedirs(...) # 创建多级目录
1 import os 2 3 file_dir = "D:\\Python_os\\os" 4 5 print (os.getcwd()) # 获取当前工作目录 6 os.chdir(file_dir) # 改变工作目录 7 print (os.getcwd()) 8 print (os.listdir(file_dir)) # 列出当前工作目录的所有文件 Python2 不支持 os.listdir() Python3 会列出当前工作目录下的所有文件 9 os.mkdir("test_mkdir") # 在当前工作目录下创建文件夹 test_mkdir;注意不可存在相同文件夹,不然会报错 10 os.makedirs("test_mkdir\\test1") 11 os.chdir(".\\test_mkdir") # . 表示本级目录; .. 表示上级目录 12 print (os.getcwd()) 13 for i in range(2,6): # 使用for循环等,可方便的创建多个文件夹 14 dir_name = "test" + str(i) 15 os.mkdir(dir_name)
在执行了上述实例代码后,os 文件夹中新建了空的 test_mkdir 文件夹,而 test_dir 文件夹下也新建出了 test1 至 test5 的空文件夹
创建文件夹可能会出错,原因具体有:(1) path 已存在时(不管是文件还是文件夹) (2) 驱动器不存在 (3) 磁盘已满 (4) 磁盘是只读的或没有写权限
删除文件夹/文件
os.rmdir(...) # 删除空文件夹 注意:必须为空文件夹 如需删除文件夹及其下所有文件,需用 shutil
os.remove(...) # 删除单一文件
shutil.rmtree(...) # 删除文件夹及其下所有文件
在上方示例的文件夹基础上,操作删除 test1 文件夹 (空文件夹可用 os.rmdir() ),删除 test_mkdir 及其下所有文件();示例代码如下
1 import os 2 import shutil 3 4 file_dir = "D:\\Python_os\\os" 5 6 ''' 删除文件/文件夹 ''' 7 os.chdir(file_dir+"\\test_mkdir") 8 print(os.getcwd()) # 确保当前工作目录 9 print(os.listdir(os.getcwd())) # 查看当前文件夹下所有文件 10 os.rmdir("test1") # 删除 test1 文件夹(空文件夹) 11 print(os.listdir(os.getcwd())) 12 os.chdir("..\\") 13 print(os.getcwd()) # 切换到上级目录 14 print(os.listdir(os.getcwd())) 15 shutil.rmtree("test_mkdir") # 删除 test_mkdir 及其下所有文件
可见运行结果如下;产生异常的可能原因: (1) 路径不存在 (2) 路径子目录中有文件或下级子目录(os.rmdir) (3) 没有操作权限或只读
只是删除单一文件,则用 os.remove("test.txt") 即可;产生异常的可能原因: (1) 文件不存在 (2) 对该文件没有操作权限或只读。
重命名文件夹/文件
可对某一文件或文件夹重命名 os.rename(oldfileName, newFilename)
在os文件夹中新建文件夹 test,文件 test.txt
1 ''' 重命名文件夹/文件 ''' 2 os.chdir(file_dir) 3 print(os.listdir(os.getcwd())) 4 os.rename("test","test1") 5 os.rename("test.txt","test1.txt") # 重命名,注意需要带扩展名 6 print(os.listdir(os.getcwd()))
可见运行结果如下;产生异常的可能原因: (1) oldfilename 旧文件名不存在(文件须带扩展名) (2)newFilename 新文件已经存在
注意:新文件的扩展名不能遗漏,理论上需要保持类型一致;但这也不失为改文件类型的一种方式(相当于直接改文件的扩展名)
复制、移动文件夹/文件
须使用 shutil 模块,引入 import shutil
shutil.copyfile("old","new") # 复制文件,都只能是文件
shutil.copytree("old","new") # 复制文件夹,都只能是目录,且new必须不存在
shutil.copy("old","new") # 复制文件/文件夹,复制 old 为 new(new是文件,若不存在,即新建),复制 old 为至 new 文件夹(文件夹已存在)
shutil.move("old","new") # 移动文件/文件夹至 new 文件夹中
我们现在 D:\ 下创建新文件夹 Python_shutil ,然后工作目录切到该目录,直接使用 Python 来操作吧,参考代码如下:
1 import os 2 import shutil 3 4 os.chdir("D:\\") 5 os.mkdir("Python_shutil") 6 file_dir = "D:\\Python_shutil" 7 os.chdir(file_dir)
为演示复制、移动操作,我们在该文件夹下手动操作 新建文件夹 test_org、 test_copy 及 test_move; 新建文件 test.txt test1.txt , 两者内容不同
Python 复制、移动的操作示例代码如下:
1 import os 2 import shutil 3 4 file_dir = "D:\\Python_shutil" 5 os.chdir(file_dir) 6 shutil.copyfile("test_org.txt","test_copy.txt") # copy test_org.txt 为 test_copy.txt 若存在,则覆盖 7 shutil.copyfile("test_org.txt","test1.txt") # 存在,覆盖 8 shutil.copytree("test_org","test_copytree") # copy test_org 为 test_copytree(不存在的新目录) 9 shutil.copy("test_org.txt","test_copy1.txt") # 同 copyfile 10 shutil.copy("test_org.txt","test_copy") # 将文件 copy 至 目标文件夹中(须存在) 11 shutil.copy("test_org.txt","test_xxx") # 将文件 copy 至 目标文件(该文件可不存在,注意类型!) 12 print os.listdir(os.getcwd()) 13 shutil.move("test_org.txt","test_move") # 将文件 move 至 目标文件夹中 14 shutil.move("test_org","test_move") # 将文件夹 move 至 目标文件夹中 15 print os.listdir(os.getcwd())
文件操作实例
复制粘贴博主的java教程中的doc文件
# Tools:Pycharm 2017.3.2 # author ="wlx" __date__ = '2018/8/13 9:43' import os import shutil path_root = 'G:\java教程\day' path_append_list = os.listdir(path_root) # 子目录集 path_joint_list = [] # 需要进入的子目录集 for p in path_append_list: # 拼接后的目录集 path_joint_list.append(path_root+"\\"+p) file_joint_list = [] # 存需要拷贝文件的地址集 for p in path_joint_list: file_list = os.listdir(p) for f in file_list: if 'source' in f: file_joint_list.append(p+"\\"+f) print(file_joint_list) file_get_list = [] for f in file_joint_list: filename_list = os.listdir(f) for filename in filename_list: if "doc" in filename: file_get_list.append(f+"\\"+filename) j = "1" for i in file_get_list: # print(os.path.isfile(i)) shutil.copy(i, 'G:\java教程\\'+j+'.doc') j = int(j) j += 1 j = str(j)
删除文件的最后一行
# Tools:Pycharm 2017.3.2 # author ="wlx" __date__ = '2018/8/12 20:37' from collections import deque deq = deque(maxlen=5) print(deq.__len__()) with open('02第二模块之三体语录', 'r+', encoding='utf-8') as f: with open('new', 'w', encoding='utf-8') as f1: while True: line = f.readline() if line: deq.append(line) if deq.__len__() == 5: f1.write(deq[0]) else: break for i in range(3): f1.write(deq[i+1])
读取文件最后一行
# Tools:Pycharm 2017.3.2 # author ="wlx" __date__ = '2018/8/13 11:03' ''' f_name为所读xx.txt文件 输出为:文件最后一行 ''' fname = 'test.txt' with open(fname, 'r') as f: # 打开文件 first_line = f.readline() # 读第一行 off = -50 # 设置偏移量 while True: f.seek(off, 2) # seek(off, 2)表示文件指针:从文件末尾(2)开始向前50个字符(-50) lines = f.readlines() # 读取文件指针范围内所有行 if len(lines) >= 2: # 判断是否最后至少有两行,这样保证了最后一行是完整的 last_line = lines[-1] # 取最后一行 break # 如果off为50时得到的readlines只有一行内容,那么不能保证最后一行是完整的 # 所以off翻倍重新运行,直到readlines不止一行 off *= 2 print('文件' + fname + '第一行为:' + first_line) print('文件' + fname + '最后一行为:' + last_line)
实现对文件结束符(EOF)的判断
法一:用sys.stdin
import sys for line in sys.stdin: a=int(line) if a!=0: print(a)
法二:用try…except
try: while True: s = input() except EOFError: pass