第十四章 文件的读取与写入(os和os.path模块、文件读取、文件写入、shutil模块、文件压缩解压缩、字符编码、剪贴板功能)
首先了解下文件夹和文件路径的概念,文件夹也可称为工作目录、目录。windows下,D:\Python\ch14\ch14_1.py称为文件路径,D:\Python\ch14称为文件夹或者工作目录。后续均以windows下路径举例说明。在操作系统中可以使用两种方式表达文件路径。一是绝对路径:路径从根目录开始表达,比如D:\Python\ch14\ch14_1.py就是绝对路径。二是相对路径:是指相对于当前工作目录的路径,若当前工作目录是D:\Python\ch14,它的相对路径是:ch14_1.py。另外,在操作系统处理文件夹中会使用2个特殊符号“.”和“..”,“.”指的是当前文件夹,“..”指的是上一层文件夹。在使用上,指当前文件夹时也可以省略“.\”。所以使用“.\ch14_1.py”与“ch14_1.py”意义相同。
一、在Python内有关文件路径的模块是os,所以在本章节最前面均需导入此模块:import os。在os模块内有另一个常用模块os.path,由于os.path是在os模块内,所以导入os模块后不用再导入os.path模块。下面列出os及os.path模块常用的文件夹和文件操作方法:
os或os.path模块常用方法 | 说明 | 示例 |
---|---|---|
os.getcwd() | 取得当前工作目录 | print(os.getcwd()) |
os.path.abspath(path) | 取得参数path的绝对路径 | print(os.path.abspath('.')) print(os.path.abspath('..')) print(os.path.abspath('ch14_01.py')) |
os.path.relpath(path, start) | 取得从start到path的相对路径,如果省略start,则返回当前工作目录至path的相对路径 | print(os.path.relpath('D:\\python')) print(os.path.relpath('D:\\', 'ch14_01.py')) |
os.path.exists(path) | 如果path的文件或文件夹存在返回True,否则返回False | print(os.path.exists('ch14_01.py')) print(os.path.exists('D:\\python\\ch14')) print(os.path.exists('../ch14')) |
os.path.isabs(path) | 如果path的文件或文件夹是绝对路径返回True,否则返回False | print(os.path.isabs('ch14_01.py')) print(os.path.isabs('D:\\python\\ch14')) |
os.path.isdir(path) | 如果path是文件夹返回True,否则返回False 如果实际文件夹不存在,返回False | print(os.path.isdir('ch14_01.py')) print(os.path.isdir('D:\\python\\ch14')) |
os.path.isfile(path) | 如果path是文件返回True,否则返回False | print(os.path.isfile('ch14_01.py')) print(os.path.isfile('D:\\python\\ch14')) |
os.mkdir(path) | 建立path目录,建议执行前先用os.path.exists( )检查是否存在 | mydir = 'testch14' if not os.path.exists(mydir): os.mkdir(mydir) |
os.rmdir(path) | 删除path目录,限制只能是空的目录。如果要删除含数据文件的目录须使用shutil模块的rmtree( )。建议执行前先用os.path.exists( )检查是否存在 | mydir = 'testch14' if os.path.exists(mydir): |
os.remove(path) | 删除path文件。建议执行前先用os.path.exists( )检查是否存在 | myfile = 'test.py' if os.path.exists(myfile): os.remove(myfile) |
os.chdir(path) | 将当前工作文件夹改至path。建议执行前先用os.path.exists( )检查是否存在 | newdir = 'D:\\python' if os.path.exists(newdir): os.chdir(newdir) print(os.getcwd()) |
os.path.join() | 将os.path.join( )参数内的字符串结合为一个文件路径,参数可以有2个到多个 | print(os.path.join('D:\\', 'python', 'ch14', 'ch14_01.py')) print(os.path.join('D:\\python', 'ch14', 'ch14_01.py')) |
os.path.getsize() | 获得特定文件的大小 | print(os.path.getsize('ch14_01.py')) print(os.path.getsize("D:\\python\\ch14\\ch14_01.py")) |
os.listdir() | 以列表方式列出特定工作目录的内容(包含当前目录下的文件和文件夹) | print(os.listdir("D:\\python\\ch14")) totalsize = 0 #列出目录下所有文件大小 for file in os.listdir("D:\\python\\ch14"): totalsize += os.path.getsize(os.path.join("D:\\python\\ch14"), file) |
os.walk() | 遍历目录树,这个方法每次执行循环时将传回3个值:①当前工作目录名称(dirName)。②当前工作目录底下的子目录列表(sub_dirNames)。③当前工作目录底下的文件列表(fileNames)。 上述dirName, sub_dirNames, fileNames名称可以自行命名,顺序则不可以更改,至于目录路径可以使用绝对地址或相对地址,如果不注明则代表当前工作目录的子目录。os.walk( )将遍历指定目录底下的子目录同时传回子目录列表和文件列表,如果所传回的子目录列表是[ ]代表底下没有子目录 | for dirName, subDirNames, fileNames in os.walk('D:\\python\\ch14'): |
Python内还有一个模块glob可用于列出特定工作目录内容glob,当导入这个模块后可以使用glob方法获得特定工作目录的内容,这个方法最大特色是可以使用通配符“*”,例如,可用“*.txt”获得所有txt扩展名的文件
import glob
#列出工作目录的所有文件
for file in glob.glob("D:\\python\\ch14\\*.*"):
print(file)
#列出工作目录的特定文件
for file in glob.glob('ch14_0*.py'):
print(file)
二、读取文件。Python处理读取或写入文件首先需将文件打开,然后可以一次读取所有文件内容或是一行一行读取文件内容。Python可以使用open( )函数打开文件,文件打开后会传回文件对象,未来可用读取此文件对象方式读取文件内容,更多有关open( )函数可参考第四章介绍。
可以使用read()读取打开的文件,所有的文件内容将以一个字符串方式被读取然后存入字符串变量内,未来只要打印此字符串变量相当于可以打印整个文件内容:
fn = 'test.txt' #相对路径,当前目录下要打开的文件名,也可以使用绝对路径
'''
python默认字符编码与操作系统有关,如果打开文件执行提示
编码错误UnicodeDecodeError,则需要加上encoding参数
encoding参数值设定与打开的文本文件编码要一致
'''
#fileObj = open(fn)
fileObj = open(fn, encoding='utf-8')
data = fileObj.read()
fileObj.close() #使用完一定要关闭已经打开的文件对象
print(data)
其实Python提供一个关键词with应用在打开文件与建立文件对象时,使用方式如下:with open(文件) as 文件对象。使用这种方式打开文件,最大特色是可以不必在程序中关闭文件,with指令会在结束不需要此文件时自动将它关闭,文件经“with open( ) as文件对象”打开后会有一个文件对象,就可以使用前一节的read( )读取此文件对象的内容:
fn = 'test.txt'
with open(fn, encoding='utf-8') as fileObj:
data = fileObj.read()
print(data)
在Python若想逐行读取文件内容,可以使用下列循环:for line in infile_obj。其中line和infile_obj变量可以自己命名。当然Python另外有一个方法readlines( )可以逐行读取,同时以列表方式储存,另一个特色是读取时每行的换行字符皆会储存在列表内,我们可以在with区块外遍历列表来获取原先文件对象内容:
fn = 'test.txt'
'''
使用for循环读取逐行读取文件,因为文本文件每行末端有换行符号,
同时print( )在输出时也有一个换行输出的符号,所以使用rstrip()
去掉每行末端的换行符
'''
with open(fn, encoding='utf-8') as file_obj:
for line in file_obj:
print(line.rstrip())
#使用readlines()逐行读取文件
with open(fn, encoding='utf-8') as file_obj:
obj_list = file_obj.readlines()
#with语句外打印列表
print(obj_list)
for line in obj_list:
print(line.rstrip())
#拼接文件中每行字符串并打印输出
str = ''
for line in obj_list:
str += line
print(str)
字符串的查找和替换。可以使用 findstr in str_obj 来查找某个子串是否在字符串中。还可以使用 index = str_obj.find(findstr, start, end) 方式来查找字符串,index是第一个搜寻到时返回的索引值,start和end代表可以被搜寻字符串的区间(区间包含start和end两个边界值),若是省略表示全部搜寻,如果没有找到返回-1给index。字符串的替换直接使用:新字符串对象 = 字符串对象.replace(旧字符串, 新字符串) 来替换,原先字符串对象内容不会被修改:
fn = 'test.txt'
with open(fn, encoding='utf-8') as file_obj:
obj_list = file_obj.readlines() #使用readlines()逐行读取文件
str = ''
for line in obj_list:
str += line #拼接每行字符串
findstr = input("请输入欲搜寻字符串:")
#使用findstr in str_obj搜寻字符串
if findstr in str:
print("%s 字符串存在 %s 文件中" % (findstr, fn))
else:
print("%s 字符串不存在 %s 文件中" % (findstr, fn))
#使用find()方法搜寻字符串
index = str.find(findstr) #搜寻字符串是否存在
if index >= 0:
print("%s 字符串存在 %s 文件中" % (findstr, fn))
print("在索引 %s 位置出现" % index)
else:
print("%s 字符串不存在 %s 文件中" % (findstr, fn))
print(str)
new_str = str.replace('\n', '') #替换字符串,将换行符去掉
print(new_str)
三、写入文件。打开文件open( )函数使用时默认是mode=‘r’读取文件模式,因此如果打开文件是供读取可以省略mode=‘r’。若是要供写入,那么就要设定写入模式mode=‘w’,程序设计时可以省略mode,直接在open( )函数内输入‘w’。如果所打开的文件需要读取和写入可以使用‘r+’。如果所打开的文件不存在,open( )会建立该文件对象,如果所打开的文件已经存在,原文件内容将被清空。为了能追加写入到文件中,用open( )打开文件时,需增加参数mode=‘a’或是用‘a’,如果所打开的文件不存在,Python会打开文件供写入,如果所打开的文件存在,Python在执行写入时不会清空原先的文件内容,而是追加写入到文件末尾。写入文件可以使用write( )方法,语法格式如下:文件对象.write(待输出数据)。
fn = 'testout.txt'
#输出字符串到文件中
string = 'I like Python'
with open(fn, 'w') as file_obj:
file_obj.write(string)
#输出数值到文件中,必须使用str()将数值转为字符串
x = 100
with open(fn, 'w') as file_obj:
file_obj.write(str(x))
#输出多行数据到文件中,如果要换行需要自己加上换行符
str1 = 'I like Python'
str2 = 'Enjoy it'
with open(fn, 'w') as file_obj:
file_obj.write(str1 + '\n')
file_obj.write(str2 + '\n')
#追加写入到文件中
with open(fn, 'a') as file_obj:
file_obj.write(str1 + '\n')
file_obj.write(str2 + '\n')
四、shutil模块。这个模块有提供一些方法可以让我们在Python程序内执行文件或目录的复制、删除、更改位置和更改名称。在使用前须加上加载模块语句:import shutil
shutil模块常用方法 | 说明 | 示例 |
---|---|---|
shutil.copy(source, destination) | 文件的复制,将source文件复制到destination目的位置,执行前source文件一定要存在否则会产生错误 | shutil.copy('test.txt', 'dest.txt') shutil.copy('D:\\python\\ch14\\test.txt', 'D:\\python') |
shutil.copytree() | 目录的复制,复制时目录底下的子目录或文件也将被复制,此外,执行前目录一定要存在否则会产生错误 | shutil.copytree('test', 'newdir') shutil.copytree('D:\\python\\ch14', 'D:\\python\\new14') |
shutil.move(source, destination) | 文件的移动,将source文件移动到destination目的位置,执行后source文件将不再存在,执行前source文件一定要存在否则会产生错误 | shutil.move('test.txt', 'D:\\python\\ch14') |
文件名的更改,source文件在移动过程中如果destination路径含有文件名或者detination目录不存在,则形成更改文件名的效果 | shutil.move('test.txt', 'hello.txt') | |
目录的移动,将source目录移动到destination目的位置,移动时source目录的子目录也将随着一起移动 | shutil.move('dir37', 'D:\\python') | |
目录名的更改,source目录在移动过程中如果destination的目录不存在,此时就可以达到目录更改名称的目的了,甚至路径名称也可能更改 | shutil.move('dir37', 'D:\\python\\out38') | |
shutil.rmtree() | os模块的rmdir( )只能删除空的目录,如果要删除含数据文件的目录须使用rmtree( ) | shutil.rmtree('dir39') #目录dir39不是空目录 |
Python内置的shutil模块在删除文件后就无法复原了,当前有一个第三方的模块send2trash,执行删除文件或文件夹后是将被删除的文件放在回收站,如果后悔可以救回。不过在使用此模块前须先下载这个外部模块,Windows下DOS环境安装此模块命令:pip install send2trash
import send2trash
send2trash.send2trash('testout.txt')
五、文件压缩解压缩。Python内有zipfile模块可以将文件或目录压缩以及解压缩。程序开头需要导入此模块: import zipfile。执行文件压缩前首先要使用ZipFile( )方法建立一份压缩后的档名,在这个方法中另外要加上‘w’参数,注明未来是供write()方法写入:fileZip = zipfile.ZipFile('out.zip', 'w')。上述fileZip和out.zip皆可以自由设定名称,fileZip是压缩文件对象代表的是out.zip,未来将被压缩的文件数据写入此对象,就可以将结果存入out.zip。虽然ZipFile( )无法执行整个目录的压缩,不过可用循环方式将目录底下的文件压缩,即可达到压缩整个目录的目的。zipfile对象有namelist( )方法可以传回zip文件内所有被压缩的文件或目录名称,同时以列表方式传回此对象。这个传回的对象可以使用infolist( )方法传回各元素的属性,如文件名filename、文件大小file_size、压缩结果大小compress_size、文件时间data_time。解压缩zip文件可以使用extractall( )方法。
import glob, os
import zipfile
#压缩文件
fileZip1 = zipfile.ZipFile('out14.zip', 'w')
for name in glob.glob('D:\\python\\ch14\\*'): #遍历ch14目录
#将文件逐个压缩到zip文件中
fileZip1.write(name, os.path.basename(name), zipfile.ZIP_DEFLATED)
fileZip1.close()
#读取压缩文件
fileZip2 = zipfile.ZipFile('out14.zip', 'r')
print(fileZip2.namelist()) #以列表形式列出所有压缩文件及目录名
for info in fileZip2.infolist(): #循环读取列表中各元素属性
print(info.filename, info.file_size, info.compress_size)
fileZip2.close()
#解压缩文件
fileUnZip = zipfile.ZipFile('out14.zip')
fileUnZip.extractall('out24') #将解压缩结果放入out24目录
fileUnZip.close()
六、字符编码。当前为止所谈到的文本文件(.txt)的打开有关文件编码部分皆是使用Windows操作系统默认方式。文本模式下常用的编码方式有utf-8和cp950。使用open( )打开文件时,可以增加另一个常用的参数encoding,整个open( )的语法将如下所示:file_obj = open(file, mode='r', encoding='utf-8')。utf-8是国际通用的编码,如果你使用Linux或Max OS,一般也是用国际编码,所以如果打开文件发生错误,请先检查文件的编码格式。使用中文Windows操作系统的记事本以utf-8执行编码时,操作系统会在文件前端增加字节顺序记号(Byte Order Mark,BOM),俗称文件前端代码,主要功能是判断文字以Unicode表示时,字节的排序方式。open( )函数使用时,也可以很明确地使用encoding=‘utf-8-sig’格式,这时即使是逐行读取也可以将BOM去除。
fn = 'bom.txt'
with open(fn, encoding='utf-8-sig') as file_obj:
obj_list = file_obj.readlines()
print(obj_list)
with open(fn, encoding='utf-8') as file_obj:
obj_list = file_obj.readlines()
print(obj_list)
七、剪贴板功能。剪贴板的功能是属第三方pyperclip模块内,使用前需使用下列方式安装此模块:pip install pyperclip,然后程序前面导入此模块:import pyperclip。安装完成后就可以使用下列两个方法:copy( ):可将列表数据复制至剪贴板。paste( ):将剪贴板数据复制回字符串变量
import pyperclip
pyperclip.copy('你好python')
string = pyperclip.paste()
print(string)