使用文件是我们几乎每天都避免的任务之一。Python包含几个内置模块,用于执行读取文件、移动文件、获取文件属性等文件操作。本文总结了许多值得了解的函数,可用于执行Python中一些最常见的文件操作,其中可以大大提高我们处理文件的效率。打开和关闭文件在读取或写入文件之前,首先要做的是打开文件。Python的内置函数open可以打开文件并返回文件对象。文件对象的类型取决于文件打开的模式,可以是文本文件对象、原始二进制文件或缓冲二进制文件对象。每个文件对象都有诸如read()和write()之类的方法。你能在下面的代码块中发现问题吗?我们稍后会知道。file=open("test_file.txt","w+")file.read()file.write("anewline")Python文档列出了所有可能的文件模式,下表列出了最常见的模式。但是要注意一个重要的规则,即:如果一个文件存在,任何与w相关的模式都会截断该文件并创建一个新文件。如果不想覆盖原文件,请慎用此模式,或尝试使用追加模式a。上一个代码块中的问题是文件打开后没有关闭。处理完文件后关闭文件很重要,因为打开的文件对象可能存在资源泄漏等不可预知的风险。以下两种方式可以保证文件正常关闭。1.使用close()第一种方法是显式使用close()。但是最好把这段代码放在最后,因为这样可以保证文件在任何情况下都可以被关闭,并且让代码更清晰。但是开发者也应该负起责任,记得关闭文件。try:file=open("test_file.txt","w+")file.write("anewline")exceptionExceptionase:logging.exception(e)finally:file.close()2.使用上下文管理器,使用open(...)asf第二种方法是使用上下文管理器。如果您对此不熟悉,还可以查看DanBader的上下文管理器和Python中的“with”语句。Withopen()asf使用__enter__和__exit__方法实现打开和关闭文件。此外,它将try/finally语句包装在上下文管理器中,因此我们不会忘记关闭文件。Withopen("test_file","w+")asfile:file.write("anewline")两种方法哪个更好?这取决于您使用的场景。以下示例实现了将50000条记录写入文件的3种不同方式。从输出中可以看出,与其他函数相比,use_context_manager_2()函数的性能极低。这是因为with语句基本上是在一个单独的函数中为每条记录打开和关闭文件,而这种繁琐的I/O操作会极大地影响性能。def_write_to_file(file,line):withopen(file,"a")asf:f.write(line)def_valid_records():foriinrange(100000):ifi%2==0:yieldidefuse_context_manager_2(file):forlinein_valid_records():_write_to_file(文件,str(line))defuse_context_manager_1(file):withopen(file,"a")asf:forlinein_valid_records():f.write(str(line))defuse_close_method(file):f=open(file,"a")forlinein_valid_records():f.write(str(line))f.close()use_close_method("test.txt")use_context_manager_1("test.txt")use_context_manager_2("test.txt")#Finisheduse_close_methodin0.0253secs#Finisheduse_context_manager_1in0.023#Finisheduse_context_manager_2in4.6302secs读写文件文件打开后,开始读写文件。文件对象提供了三种读取文件的方法,分别是read()、readline()和readlines()。默认情况下,read(size=-1)返回文件的全部内容。但是如果文件大于内存,可选参数size可以帮助限制返回的字符(文本模式)或字节(二进制模式)大小。readline(size=-1)返回整行,包括末尾的字符n。如果size大于0,它将返回行中的最大字符数。readlines(hint=-1)返回列表中文件的所有行。如果返回的字符数超过可选参数提示,则不会返回任何行。在上述三种方法中,read()和readlines()的内存效率较低,因为它们默认将完整文件作为字符串或列表返回。一种更有效的遍历内存的方法是使用readline()并让它停止读取,直到返回一个空字符串。空字符串“”表示指针已到达文件末尾。withopen(test.txt,r)asreader:line=reader.readline()whileline!="":line=reader.readline()print(line)以节省内存的方式读取文件有两种写法:写()和writelines()。顾名思义,write()写入一个字符串,而writelines()写入一个字符串列表。开发人员必须在末尾添加n。withopen("test.txt","w+")asf:f.write("hi")f.writelines(["thisisaline","thisisanotherline"])#>>>cattest.txt#hi#thisisaline#thisisanotherlineinWriting文件中的行要将文本写入特殊文件类型(例如JSON或csv),您应该在文件对象之上使用Python内置模块json或csv。importcsvimportjsonwithopen("cities.csv","w+")asfile:writer=csv.DictWriter(file,fieldnames=["city","country"])writer.writeheader()writer.writerow({"city":"阿姆斯特丹","country":"荷兰"})writer.writerows([{"city":"柏林","country":"德国"},{"city":"上海","country":"中国"},])#>>>catcities.csv#city,country#Amsterdam,Netherlands#Berlin,Germany#Shanghai,Chinawithopen("cities.json","w+")asfile:json.dump({"city":"Amsterdam","country":"Netherlands"},file)#>>>catcities.json#{"city":"Amsterdam","country":"Netherlands"}在文件中移动指针打开文件时,它将获取指向特定位置的文件处理程序。在r和w模式下,处理程序指向文件的开头。在一种模式下,处理程序指向文件的末尾。tell()和seek()读取文件时,如果指针没有移动,指针会自己移动到下一个位置开始读取。以下2个方法可以做到这一点:tell()和seek()。tell()返回指针的当前位置,作为从文件开头算起的字节/字符数。seek(offset,whence=0)移动处理程序使字符远离wherece。wherece可以是:0:从文件头开始1:从当前位置开始2:从文件尾开始文本模式下,wherece只能为0,offset必须≥0。withopen("text.txt","w+")asf:f.write("0123456789abcdef")f.seek(9)print(f.tell())#9(pointermovesto9,nextreadstartsfrom9)print(f.read())#9abcdeftell()和seek()了解文件状态操作系统中的文件系统有很多关于文件的有用信息,例如:文件的大小,创建和修改的时间。要在Python中获取此信息,您可以使用os或pathlib模块。实际上,os和pathlib有很多共同点。但后者更面向对象。os使用os.stat("test.txt")获取文件的完整状态。它可以返回一个结果对象,其中包含许多统计信息,例如st_size(以字节为单位的文件大小)、st_atime(最后访问的时间戳)、st_mtime(最后修改的时间戳)等。print(os.stat("text.txt"))>>>os.stat_result(st_mode=33188,st_ino=8618932538,st_dev=16777220,st_nlink=1,st_uid=501,st_gid=20,st_size=16,st_atime=1597527409,st_mtime=1597527409,st_ctime=1597527409)可以得到统计信息单独使用os.path。os.path.getatime()os.path.getctime()os.path.getmtime()os.path.getsize()Pathlib也可以使用pathlib.Path("text.txt").stat()来获取完整的文件的状态。它可以返回与os.stat()相同的对象。print(pathlib.Path("text.txt").stat())>>>os.stat_result(st_mode=33188,st_ino=8618932538,st_dev=16777220,st_nlink=1,st_uid=501,st_gid=20,st_size=16,st_atime=1597528703,st_mtime=1597528703,st_ctime=1597528703)下面将从多方面比较os和pathlib的异同。复制、移动和删除文件Python有许多处理文件移动的内置模块。在您相信Google返回的第一个答案之前,您应该了解不同模块选择的性能会有所不同。一些模块阻塞线程直到文件移动完成;其他人可能异步执行。shutilshutil是最著名的用于移动、复制和删除文件(文件夹)的模块。它有3种仅用于复制文件的方法:copy()、copy2()和copyfile()。复制()与copy2():copy2()与copy()非常相似。但不同的是,前者还可以复制文件的元数据,例如最近的访问时间和修改时间。但是,由于Python文档操作系统的限制,即使是copy2()也无法复制所有元数据。shutil.copy("1.csv","copy.csv")shutil.copy2("1.csv","copy2.csv")print(pathlib.Path("1.csv").stat())打印(pathlib.Path("copy.csv").stat())print(pathlib.Path("copy2.csv").stat())#1.csv#os.stat_result(st_mode=33152,st_ino=8618884732,st_dev=16777220,st_nlink=1,st_uid=501,st_gid=20,st_size=11,st_atime=1597570395,st_mtime=1597259421,st_ctime=1597570360)#copy.csv#os.stat_result(st_mode=33152,st_ino8618983930,st_dev=16777220,st_nlink=1,st_uid=501,st_gid=20,st_size=11,st_atime=1597570387,st_mtime=1597570395,st_ctime=1597570395)#copy2.csv#os.stat_result=9=3dev72st_1587270387,st_nlink=1,st_uid=501,st_gid=20,st_size=11,st_atime=1597570395,st_mtime=1597259421,st_ctime=1597570395)copy()vs.copy2()copy()对比copy(file()):)可以设置新文件的权限与原文件相同,但copyfile()不会复制其权限模式。其次,copy()的目标可以是一个目录。如果存在同名文件,则覆盖原文件或创建新文件。但是,copyfile()的目标必须是目标文件名。shutil.copy("1.csv","copy.csv")shutil.copyfile("1.csv","copyfile.csv")print(pathlib.Path("1.csv").stat())打印(pathlib.Path("copy.csv").stat())print(pathlib.Path("copyfile.csv").stat())#1.csv#os.stat_result(st_mode=33152,st_ino=8618884732,st_dev=16777220,st_nlink=1,st_uid=501,st_gid=20,st_size=11,st_atime=1597570395,st_mtime=1597259421,st_ctime=1597570360)#copy.csv#os.stat_result(st_mode=33152,st_ino8618983930,st_dev=16777220,st_nlink=1,st_uid=501,st_gid=20,st_size=11,st_atime=1597570387,st_mtime=1597570395,st_ctime=1597570395)#copyfile.csv#permission(st_modest_stat_reult)#os.=33188,st_ino=8618984694,st_dev=16777220,st_nlink=1,st_uid=501,st_gid=20,st_size=11,st_atime=1597570387,st_mtime=1597570395,st_ctime=1597570395)shutil.copyfile","1.csv./source")#IsADirectoryError:[Errno21]Isadirectory:./sourcecopy()vs.copyfile()osos模块内含system()函数,要在子shell中执行命令,需要将命令作为参数传递给system(),这与在操作系统上执行命令的效果是一样的。对于移动和删除文件,os模块中也提供了专用函数。#copyos.system("cp1.csvcopy.csv")#rename/moveos.system("mv1.csvmove.csv")os.rename("1.csv","move.csv")#deleteos.system("rmmove.csv")异步复制/移动文件到目前为止,解决方案始终是同步执行的,这意味着如果文件太大并且移动需要更多时间,则程序可能会终止。如果要异步执行程序,可以使用线程、多处理或子进程模块,它们使文件操作能够在单独的线程或进程中运行。importthreadingimportsubprocessimportmultiprocessingsrc="1.csv"dst="dst_thread.csv"thread=threading.Thread(target=shutil.copy,args=[src,dst])thread.start()thread.join()dst="dst_multiprocessing.csv"process=multiprocessing.Process(target=shutil.copy,args=[src,dst])process.start()process.join()cmd="cp1.csvdst_subprocess.csv"status=subprocess.call(cmd,shell=True)异步执行文件操作搜索文件复制和移动文件后,您可能需要搜索与特定模式匹配的文件名。Python提供了许多内置函数可供选择。Globglob模块根据Unixshell使用的规则查找与指定模式匹配的所有路径名,它支持使用通配符。glob.glob("*.csv")在当前目录中搜索所有扩展名为csv的文件。使用glob模块,也可以在子目录中搜索文件。>>>importglob>>>glob.glob("*.csv")[1.csv,2.csv]>>>glob.glob("**/*.csv",recursive=True)[1.csv,2.csv,source/3.csv]osos模块很强大,它基本上可以执行所有文件操作。我们可以简单地使用os.listdir()列出目录中的所有文件,并使用file.endswith()和file.startswith()检测模式,以及os.walk()遍历目录.importosforfileinos.listdir("."):iffile.endswith(".csv"):print(file)forroot,dirs,filesinos.walk("."):forfileinfiles:iffile.endswith(".csv"):print(file)searchfilenames-ospathlibpathlib的功能类似于glob模块。它还可以递归地搜索文件名。pathlib比上面基于os的解决方案代码更少,并且提供了更面向对象的解决方案。frompathlibimportPathp=Path(".")fornameinp.glob("**/*.csv"):#recursiveprint(name)搜索文件名-pathlib管理文件路径管理文件路径是另一个常见的执行任务。它可以获取文件的相对路径和绝对路径,也可以连接多个路径和查找父目录等。相对路径和绝对路径都可以os和pathlib都可以获取文件或目录的相对路径和绝对路径.importosimportpathlibprint(os.path.abspath("1.txt"))#absoluteprint(os.path.relpath("1.txt"))#relativeprint(pathlib.Path("1.txt").absolute())#absoluteprint(pathlib.Path("1.txt"))#文件的相对和绝对路径连接路径这就是我们如何独立于环境连接os和pathlib中的路径。pathlib使用斜杠来创建子路径。importosimportpathlibprint(os.path.join("/home","file.txt"))print(pathlib.Path("/home")/"file.txt")链接文件路径获取父目录dirname()在ospathlib中获取父目录的函数,直接使用Path().parent函数获取父文件夹即可。importosimportpathlib#relativepathprint(os.path.dirname("source/2.csv"))#sourceprint(pathlib.Path("source/2.csv").parent)#source#absolutepathprint(pathlib.Path("source/2.csv").resolve().parent)#/Users/<...>/project/sourceprint(os.path.dirname(os.path.abspath("source/2.csv")))#/Users/<...>/project/source获取父文件夹最后简单介绍一下os和pathlib。正如Python文档中所述,pathlib是一个比os.pathlib更面向对象的解决方案。它将每个文件路径表示为适当的对象,而不是字符串。这对开发者来说非常方便:它提供了对对象的直接访问,简化了连接多条路径的过程,并使得跨不同操作系统的操作更加一致。搞定这些操作,这样文件处??理的效率就up了!本文转载自微信公众号“读书芯”,可通过以下二维码关注。转载本文请联系芯读公众号。
