[Python]-11-文件读写

引言

这篇文章介绍如何使用python的os与shutil模块,对文件或文件夹进行读写、创建、删除等操作。

文章目录

0×1.文件相关操作

a.创建文件

Python中创建文件是通过内置的open()函数实现的,这个函数接收最基本的两个参数,第一个为文件路径(相对或绝对路径),第二个是对文件的操作方式(读写),创建文件有下面两种语法:

语法一:file1=open("文件路径","w")

语法二:with open("文件路径","w") as file1:

其中file1是文件对象名称,参数"w"参数告诉python解释器,创建一个可写入数据的文件,并在关闭数据流前,保持可写入状态,语法二是python3推荐的标准方法。

文件路径可以是相对路径或绝对路径,相对路径是相对当前运行的脚本目录的,windows下文件目录中的反斜杠需要使用转义输出,例如:"C:\\windows\\system32\\filename.txt";或使用参数前缀"r"取消字符串中对反斜杠的特殊处理,例如:r"c:\windows\system32\filename.txt";本文所有的操作都使用ubuntu系统举例:

					#!/usr/bin/env python
					#coding=utf-8
					#使用语法一,在"/home/qing/test/"目录中创建qingsword.txt文件,如果文件不存在则创建,如果文件存在则覆盖,file1.write()往这个文件中写入了一行数据;如果不添加参数"w"则文件使用只读方式传递给file1对象,在只读模式下,如果文件不存在则会触发"FileNotFoundError"异常。
					file1=open("/home/qing/test/qingsword.txt","w")
					file1.write("www.qingsword.com")
					file1.close()  #关闭文件流
					del file1      #删除文件对象释放资源

					#如果要输入多行数据,可以使用三引号,python会根据三引号中的格式将每行数据写入文件中,三引号有点类似HTML语言中的pre标签

					#使用语法二,完成上面的操作(python3推荐)
					#!/usr/bin/env python3
					#coding=utf-8
					with open("/home/qing/test/qingsword.txt","w") as file1:
					    file1.write("""晴刃
					qingsword
					www.qingsword.com""")
					

注意到上面的两种语法,语法二并没有使用close()函数来关闭文件流,这是因为with表达式内部包含两个重要函数,__enter__和__exit__,__enter__函数负责将open()函数的返回值赋予as后面的对象(file1),最后不论该语句块出现了什么异常,都会在离开时执行__exit__函数,该函数会自动逐个关闭打开的文件对象;这样设计的好处显而易见,在语法一中,如果在close()函数执行前,程序出现异常退出了,那么这个文件流不会被正常关闭,将会一直占用着系统资源,所以推荐使用with来打开一个文件。

本例使用语法二,像文件中输入了三行数据,下面来看看如何读取这些数据内容。

b.读取文件内容

读取文件可以使用文件对象的read()和readline()两个方法,read()会一次性读取所有的文件内容,readline()一次只读一行,在读取内容时,如果文本中有换行符(\n)也会被读取到,但在输出或写入其他文件时,这些换行符会被自动转换成换行,请看下面的实例:

					#!/usr/bin/env python3
					#coding=utf-8
					import os  #导入os模块
					#os.path.isfile()函数能够判断传入的路径是否为文件,open函数中的参数"r"表示以只读方式打开文件
					if os.path.isfile("/home/qing/test/qingsword.txt"):
					    with open("/home/qing/test/qingsword.txt","r") as file1:
					        line1=file1.readline() #读取一整行(遇到回车符\n终止)
					        print(line1)
					        line1=file1.readline()
					        print(line1)
					        line1=file1.readline()
					        print(line1)

					#程序输出,输出中每行之间空了一行,这是因为"晴刃"和"qingsword"后面有都有一个换行符"\n",输出文本数据后光标被移动到了下一行,而print()函数默认情况下会新起一行输出,所以又会再往下移动一行,才会得到下面这样的结果
					晴刃

					qingsword

					www.qingsword.com

					#如果想去掉换行符,可以使用strip("\n")筛选掉每行前后的换行符,如下
					#!/usr/bin/env python3
					#coding=utf-8
					import os
					if os.path.isfile("/home/qing/test/qingsword.txt"):
					    with open("/home/qing/test/qingsword.txt","r") as file1:
					        line1=file1.readline().strip("\n")
					        print(line1)
					        line1=file1.readline().strip("\n")
					        print(line1)
					        line1=file1.readline().strip("\n")
					        print(line1)

					#输出
					晴刃
					qingsword
					www.qingsword.com

					#如果一行的数据太长,一次性读取可能会导致内存溢出,使用readline(字符数)可以读取指定的字符数,例如
					#!/usr/bin/env python3
					#coding=utf-8
					import os
					if os.path.isfile("/home/qing/test/qingsword.txt"):
					    with open("/home/qing/test/qingsword.txt","r") as file1:
					        line1=file1.readline(2) #获取"晴刃"两个字符
					        print(line1)
					        line1=file1.readline() #获取到"\n"回车符,当readline遇到回车符时,指定多少字符都没有意义,因为readline遇到回车符后就返回,不会接着往下读取
					        print(line1)
					        line1=file1.readline(4) #再获取接下来4个字符"qing"
					        print(line1)

					#输出,根据前面的解释,很容易明白为什么两行数据间会空两行了
					晴刃


					qing
					

除了使用上面的方式读取文件内容外,还有一种读取方式,使用readlines()函数,它能够读取文件中的每一行数据并保存为一个列表,每行数据相当于列表的一个元素,可以遍历列表每个元素输出每一行数据,或打印出每一行的字符数,请看下面的实例:

					#!/usr/bin/env python3
					#coding=utf-8
					import os
					if os.path.isfile("/home/qing/test/qingsword.txt"):
					    with open("/home/qing/test/qingsword.txt","r") as file1:
					        lines=file1.readlines()
					        print(lines)
					        for line in lines:
					            #输出每一行的字符数(不包括回车符)
					            print(len(line.strip("\n")))

					#程序输出
					['晴刃\n', 'qingsword\n', 'www.qingsword.com']
					2
					9
					17
					

with不仅仅能打开单个文件,还能一次性打开多个文件,如下:

					#!/usr/bin/env python3
					#coding=utf-8
					import os
					try: #添加错误处理
					    if os.path.isfile("hello.py") and os.path.isfile("if.py"):
					        #使用with来打开一个或多个文件(可以用逗号分隔任意多个文件),使用with的好处是,不需要调用close()函数,with会在区块运行结束后自动调用close()关闭这些文件;read()函数能够一次性读取文件流当前指针所在位置,到文件结尾的所有内容
					        with open("hello.py","r") as f1,open("if.py") as f2:
					            print(f1.read())
					            for line in f2.readlines():
					                #不带任何参数的strip()函数能够去掉字符串前后空格以及换行符与制表符等
					                print(line.strip())
					#如果os操作过程中遇到错误会被捕获,本例并没有对可能出现的os错误做细分处理
					except Exception as erro:
					    pass
					

如果我们读取的不是一个文本文件,是一个压缩包文件,一张图片,或一个视频文件,这个时候就需要使用"rb"参数来读取二进制数据流,例如:

					#!/usr/bin/env python3
					#coding=utf-8
					import os
					try:
					    if os.path.isfile("cat.jpeg"):
					        with open("cat.jpeg","rb") as img1:
					            #读取当前目录下"cat.jpeg"图片文件二进制数据流的前10个字节
					            print(img1.read(10))
					except Exception as erro:
					    pass

					#程序输出
					b'\xff\xd8\xff\xe0\x00\x10JFIF'
					

使用参数r来读取文本文件默认是以utf-8编码读取的,如果需要读取其他编码的文件,需要在open命令中添加字符编码,例如:

					#!/usr/bin/env python3
					#coding=utf-8
					import os
					try:
					    if os.path.isfile("testfile"):
					        #假设testfile是以gbk编码的,如果文件读取过程中遇到编码错误,直接忽略错误,如果不添加errors关键字,遇到编码错误时,会抛出UnicodeDecodeError异常
					        with open("testfile","r",encoding="GBK",errors="ignore") as f1:
					            print(f1.read(1))
					except Exception as erro:
					    pass
					

c.文件内容追加

在上面内容的基础上,现在我们往qingsword.txt这个文件中追加内容,请看下面的实例:

					#!/usr/bin/env python3
					#coding=utf-8
					import os
					file_path="/home/qing/test/qingsword.txt"
					if os.path.isfile(file_path):
					    #"a"参数表示打开文件并追加内容
					    #如果不想追加的内容顶在上一次内容的后面,可以写入一个回车符,或使用"三引号"换行输入,在使用"三引号"的时候注意,文本的缩进也会被保留,所以本例的输入都是顶格的
					    with open(file_path,"a") as file1:
					        file1.write("""
					欢迎大家到我的博客学习:
					www.qingsword.com""")
					    with open(file_path,"r") as file1:
					        print(file1.read()) #读取文件所有内容并输出

					#程序输出
					晴刃
					qingsword
					www.qingsword.com
					欢迎大家到我的博客学习:
					www.qingsword.com
					

现在,上面的内容已经可以让我们将一个文件的内容复制出来追加到另外一个文件的末尾了,下面是一个简单的实现:

					#!/usr/bin/env python3
					#coding=utf-8
					import os
					#创建两个文件,分别写入数据
					file_path_1="/home/qing/test/qingsword_1.txt"
					file_path_2="/home/qing/test/qingsword_2.txt"
					with open(file_path_1,"w") as file1,\
					     open(file_path_2,"w") as file2:
					    file1.write("""欢迎大家到我的博客学习:
					www.qingsword.com""")
					    file2.write("晴刃")

					#将file_path_2文件中的内容追加到file_path_1文件内容下
					if os.path.isfile(file_path_1) and\
					   os.path.isfile(file_path_2):
					    with open(file_path_2,"r") as file1,\
					         open(file_path_1,"a") as file2:
					        file2.write("\n")
					        file2.write(file1.read())
					
					#打印出file_path_1文件内容
					if os.path.isfile(file_path_1):
					    with open(file_path_1,"r") as file1:
					        print(file1.read())

					#程序输出
					欢迎大家到我的博客学习:
					www.qingsword.com
					晴刃
					

d.文件读写异常

在对文件读写操作的时候,会出现下面这些常见的异常,如果希望程序更加的"友好",我们应该提前了解并使用try语句块捕获这些异常,下面列举三个比较常见的异常,这些异常都属于OSError异常类;

FileNotFoundError:当以只读方式打开一个不存在的文件,或路径不存在时,或文件操作过程中找不到某文件时触发;

PermissionError:试图在一个没有权限的目录中读写文件时触发;

IsADirectoryError:试图往一个不存在的文件夹复制文件时触发;

e.获取文件扩展名

os.path.splitext()函数可以用于获取文件的扩展名,请看下面的实例:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					print(os.path.splitext("qingsword.exe.txt"))
					print(os.path.splitext("qingsword.exe.txt")[1])

					#程序输出,os.path.splitext()函数可以将传入的文件名称的最后一个点后面的扩展名和前面的文件名称分割成一个元组,通过访问这个元组索引位置为1的元素,就能得到文件的扩展名
					('qingsword.exe', '.txt')
					.txt
					

f.获取文件绝对路径

os.path.abspath()函数可用于获取文件的绝对路径,这个函数无法判断传入的文件是否存在,就算这个文件不存在,仍然可以使用这个函数,函数会将文件名和当前脚本执行的绝对路径目录拼接起来:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					print(os.path.abspath("qingsword.txt"))

					#程序输出
					/home/qing/python-lab/qingsword.txt

					#如果是获取当前脚本所在目录的绝对路径,可以使用下面的语法
					os.path.abspath(".")
					

g.获取文件大小

可以使用os.path.getsize()函数获取文件的大小,单位为byte:

					import os
					print(os.path.getsize("/home/qing/test/qingsword.txt"))

					#输出,qingsword.txt只有89字节
					89
					

h.获取文件最新修改时间

os.path.getmtime()函数能够得到文件从1970年起到上次修改之间经过的秒数,使用time模块的ctime()方法,能够将这个秒数格式化为标准时间输出:"星期 月 日 时:分:秒 年"

					#!/usr/bin/env python
					#coding=utf-8
					import os
					import time
					file_path="/home/qing/test/qingsword.txt"
					print(time.ctime(os.path.getmtime(file_path)))

					#程序输出
					Mon Aug 29 17:01:00 2016
					

i.文件的移动复制与删除

python中可以使用shutil模块来实现文件的移动与复制,请看下面的实例:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					import shutil
					if os.path.isfile("qing.txt"):
					    #将当前脚本运行目录下的qing.txt文件复制并重命名为qingsword.txt
					    shutil.copy("qing.txt","qingsword.txt") 
					    os.remove("qing.txt") #删除文件
					#isdir()函数能够判断目标是否为目录
					if os.path.isfile("qingsword.txt") and os.path.isdir("pack1"):
					    #将刚才复制的文件剪切移动到pack1目录下并重命名为qingsword.py
					    shutil.move("qingsword.txt","pack1/qingsword.py")
					

0×2.文件夹相关操作

a.拼接路径

os.path.join()函数用于拼接路径,它不会判断两个路径是否存在,仅仅完成将前面的路径和后面的路径进行拼接的功能,请看下面的实例:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					path1="/www"
					path2="qingsword/com"
					print(os.path.join(path1,path2))

					#输出
					/www/qingsword/com
					

b.获取子目录名

有时候我们可能需要获取某个路径下最后一个子目录的名称,python提供了一个os.path.split()方法用于切割路径,请看下面的实例:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					path1="/www/qingsword/com"
					#split()方法返回一个元组,包含两个元素,父路径和子目录名
					print(os.path.split(path1))
					#可以直接使用两个对象来接收元组中这两个元素
					p1,p2=os.path.split(path1)
					print(p1)
					print(p2)

					#输出
					('/www/qingsword', 'com')
					/www/qingsword
					com
					

如果想递归获取路径中每一层目录的名称,可以使用下面的方法:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					#------
					def split_fully(path):
					    """递归获取路径中每一层目录的名称,返回一个元组"""
					    parent_path,dir_name=os.path.split(path)
					    if dir_name=="":
					        return (parent_path,)
					    else:
					        return split_fully(parent_path)+(dir_name,)

					path1="/www/qingsword/com"
					for p in split_fully(path1):
					    print(p)

					#输出
					/
					www
					qingsword
					com
					

在上面这个实例中,程序的运行流程是这样的,split_fully()函数首先将"/www/qingsword/com"路径分割成"/www/qingsword"与"com",因为dir_name不为空,所以执行return split_fully(parent_path)+(dir_name,),这一句中(dir_name,)就相当于(com,)而前半段使用父路径再次调用函数本身,外层函数将被挂起等待split_fully(parent_path)结果的返回,这一次的调用parent_path="/www/qingsword";

第一次调用自身"/www/qingsword"被分割成"/www"和"qingsword",由于dir_name不为空,函数第二次调用自身,此时如果我们将所有变量都代入数据,返回最外层被挂起的函数查看return,应该是这样的结构return split_fully("/www")+(qingsword,)+(com,);

第二次调用自身,"/www"被分割成parent_path="/",dir_name="www"不为空,程序会第三次调用自身,这一次parent_path,dir_name=os.path.split(path)分割后,dir_name为空,所以返回parent_path="/",最后一层一层返回,最外层的结构就变成了return ("/",)+("www",)+("qingsword",)+("com",),只需要遍历这个元组,就能得到每个元素的值。

c.规范化路径输出

在路径中,"."表示当前目录,".."表示父目录,如果我们不希望这些符号出现,可以使用os.path.normpath()函数,它不仅可以规范路径,将两个点自动解析成父目录,还能去除路径中多余的斜杠,请看下面的实例:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					path1="///www/./qingsword////hidden/..//com"
					print(os.path.normpath(path1))

					#normpath会自动清除目录中多余的斜杠,并且将..解析为父目录,所以hidden目录后面的..就相当于在hidden目录里面执行了一个"cd .."命令,返回了qingsword目录中,而www目录后面的.就代表当前目录,所以直接去掉了,最后输出如下
					/www/qingsword/com
					

d.判断目录或文件是否存在

os.path.exists()函数能够判断目标是否存在,但不能判断目标是文件还是目录,请看下面的实例:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					path1="/www/qingsword/com"
					print(os.path.exists(path1))
					print(os.path.exists("textfile"))

					#输出,如果目标存在就返回True,不存在就返回False
					True
					False
					

e.列出目录下所有文件

使用os.listdir()函数,可以返回目标目录下所有文件夹和文件的名称列表,请看下面的实例:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					#获取当前脚本所在目录的绝对路径
					file_path=os.path.abspath(".")

					#遍历列表,打印出每个文件或文件夹的名称
					for name in os.listdir(file_path):
					    print(name)

					#如果想打印出目录下每个文件或文件夹的完整路径,可以修改print函数,下面两种方法效果相同
					print(file_path+"/"+name)
					print(os.path.join(file_path,name))
					

如果想要对listdir的输出进行排序,可以使用sorted()函数,默认按照字母顺序排列,并且区分大小写,大写字母>符号>小写字母,这样for中的name会按照排序后的顺序读取并拼接:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					file_path=os.path.abspath(".")
					#排序输出
					for name in sorted(os.listdir(file_path)):
					    print(os.path.join(file_path,name))
					

上面的实例仅仅能够遍历某个目录下的文件和目录,但不能进行递归查询,也就是说,这个目录下的子目录中的文件是无法被搜索到的,下面提供一种遍历子目录文件的方法:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					#------
					def print_tree(path):
					    """递归目录"""
					    if os.path.isdir(path):
					        #遍历目录,打印绝对路径
					        for name in os.listdir(path):
					            abs_path=os.path.join(path,name)
					            print(abs_path)
					            #如果路径为目录,调用自身,打印子目录内容
					            if os.path.isdir(abs_path):
					                print_tree(abs_path)

					print_tree("/home/qing/test")
					

再来看一个实例,读取目录下的文件,显示每个文件的绝对路径,大小和最后修改时间:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					import time
					#------
					def list_dir(path):
					    """打印目录中文件或文件夹的大小,最后修改时间和绝对路径"""
					    if os.path.isdir(path):
					        for name in os.listdir(path):
					            abs_path=os.path.join(path,name)
					            sz=os.path.getsize(abs_path)
					            modify_time=time.ctime(os.path.getmtime(abs_path))
					            print("Size:%-8d Last Modify Time:%-25s Path:%s"%(sz,modify_time,abs_path))

					list_dir("/home/qing/test")
					

利用列表生成式可以很简洁的打印出某一类型的文件列表,例如:

					#!/usr/bin/env python3
					#coding=utf-8
					import os
					#列出脚本所在文件夹中所有文件和文件夹名称
					print([x for x in os.listdir(os.path.abspath("."))])

					#列出脚本所在文件夹中所有子文件夹名称
					print([x for x in os.listdir(os.path.abspath("."))\
					       if os.path.isdir(x)])

					#列出脚本所在文件夹中所有py文件名称
					print([x for x in os.listdir(os.path.abspath("."))\
					       if os.path.isfile(x) and os.path.splitext(x)[1]==".py"])
					

f.创建删除文件夹

创建文件夹:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					#os.mkdir()函数一次只能创建一个文件夹
					if not os.path.exists("qingsword"):
					    os.mkdir("qingsword")
					#os.makedirs()函数能够一次性递归创建多个文件夹
					if os.path.isdir("qingsword"):
					    os.makedirs("qingsword/1/2/3/4")
					

Ps:在创建文件夹的过程中,如果目标存在,则会抛出一个"FileExistsError"异常。

删除文件夹:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					import shutil

					if os.path.isdir("qingsword"):
					    os.rmdir("qingsword/1/2/3/4")
					    shutil.rmtree("qingsword")
					

Ps:os.rmdir()函数只能删除单个文件夹且文件夹下必须为空,否则会抛出一个"OSError"异常,而shutil模块的rmtree方法能够一次性删除目录及下面的所有文件。

g.通配符的使用

在python路径操作中,有下面这些比较常用的通配符:

* 匹配多个字符;

? 匹配单个字符;

[...] 匹配括号中的单个字符,可以指定多个字符或字符范围,例如:[ABCD]代表ABCD任意一个都能匹配;[a-z]匹配小写字母表中任意一个字符;[0-9]匹配0-9数字中的任意一个;

[!...]不匹配括号中指定的任意一个或某个范围中的一个;

在python中有一个通配符模块glob,这个模块的glob()函数同os.listdir()类似,都能返回某个目录下的文件,但glob.glob()函数更加强大,它能够配合通配符返回指定的文件类型,请看下面的实例:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					import glob
					path1="/home/qing/test"
					if os.path.isdir(path1):
					    #返回path1目录下所有py后缀的文件
					    print(glob.glob(os.path.join(path1,"*.py")))
					    #返回文件名最后一个字符在a-d范围内的py文件
					    print(glob.glob(os.path.join(path1,"*[a-d].py")))
					    #返回文件名为四个字符的py文件
					    print(glob.glob(os.path.join(path1,"????.py")))
					    返回文件名为三个字符且最后一个字符不在b-f范围内的py文件
					    print(glob.glob(os.path.join(path1,"??[!b-f].py")))
					    #这是glob函数强大的地方,递归path1目录,返回目录以及子目录中所有py文件
					    print(glob.glob(os.path.join(path1,"*/*.py")))
					

Ps:glob.glob()函数返回类型为列表。

h.文件和文件夹重命名

os模块的rename方法可以实现文件和文件夹重命名的操作,请看下面的实例:

					#!/usr/bin/env python3
					#coding=utf-8
					import os
					#如果file1存在且file2不存在,将file1改名为file2,file1可以是文件或文件夹的绝对或相对路径
					if os.path.exists("file1") and not os.path.exists("file2"):
					    os.rename("file1","file2")
					

i.文件夹复制

可以使用shutil模块的copytree方法来拷贝一个目录到目标位置,例如:

					#!/usr/bin/env python3
					#coding=utf-8
					import os
					import shutil
					def copyDir(src,dst):
					    """完成源文件夹到目标位置的复制"""
					    if os.path.isdir(src) and not os.path.exists(dst):
					        shutil.copytree(src,dst)
					        print("复制成功。")
					    else:
					        print("复制失败。")
					def moveDir(src,dst):
					    """完成源文件夹到目标位置的移动"""
					    if os.path.isdir(src) and not os.path.exists(dst):
					        shutil.copytree(src,dst)
					        shutil.rmtree(src)
					        print("移动成功。")
					    else:
					        print("移动失败。")    

					moveDir("dir1","dir2")
					copyDir("dir1","dir2") 
					

0×3.实例:日志文件轮换

有时候我们可能需要不断的写入一些日志,每次写入的时候,会产生一个新的版本,新版本永远是不带文件号的,然后老版本会被复制成带尾号的样子,例如:qingsword.log,qingsword.log.1,qingsword.log.2,不带数字的是最新版本,2为最旧的版本,再次写入时2版本会被复制成3版本,1版本复制成2,最近一次保存会被复制成1,但是每次这样手动操作太麻烦了,如果文件量巨大耗时耗力,那么如何让程序自动来完成这些数字的轮换呢,请看下面这个实例:

					#!/usr/bin/env python
					#coding=utf-8
					import os
					import shutil
					import time

					#------
					def make_version_path(path,version):
					    """返回版本文件路径"""
					    if version==0:
					        return path
					    else:
					        return path+"."+str(version)
					#------
					def rotate(path,version=0):
					    """完成文件轮换"""
					    if version>4:
					        return
					    old_path=make_version_path(path,version)
					    if not os.path.exists(old_path):
					        raise OSError("'%s' doesn't exist."%old_path)
					    new_path=make_version_path(path,version+1)
					    if os.path.exists(new_path):
					        rotate(path, version+1)
					    shutil.copy(old_path,new_path)
					#------
					def rotate_log_file(path):
					    """创建新日志文件"""
					    if os.path.exists(path):
					        with open(path,"a") as file1:
					            file1.write("\n"+time.ctime(time.time()))
					    else:
					        with open(path,"w") as file1:
					            file1.write(time.ctime(time.time()))
					    rotate(path)

					rotate_log_file("/home/qing/test/qingsword/qingsword.log")

					#运行程序后,"/home/qing/test/qingsword/"下会创建一个"qingsword.log"文件,反复运行这个程序,会发现目录中的文件数量最多为6个,最大的文件编号为"qingsword.log.5",这是因为在rotate()方法中做了最大版本号限制,如果version高于4,那么就结束调用自身,这段程最后可以完成文件版本从,4>5,3>4,2>3,1>2,0>1的版本复制操作。
					

0×4.内存文件读写

有时我们不需要将数据写入硬盘,只需要在内存中创建一个临时文件流,暂时保存一些数据,这个时候就可以用到StringIO和BytesIO,前者用来读写字符串数据,后者用来读写二进制数据,请看下面的实例:

					#!/usr/bin/env python3
					#coding=utf-8
					#导入内存读写模块
					from io import StringIO,BytesIO

					#初始化一个空内存字符串读写对象
					with StringIO() as si:
					    #同文件读写一样,写入数据
					    si.write("""My website:
					www.qingsword.com""")
					    #查看当前文件流指针位置,写入后指针在文件流的末尾
					    print(si.tell())  #29
					    #将指针指向第一个字符
					    si.seek(0)
					    #读取全部内容(如果没有使用seek(0),这一步将读取不到任何数据)
					    print(si.read())

					    #读取完成后,指针停留在文件流末尾
					    print(si.tell()) #29
					    #将指针移动到第三个字符的位置开始读取
					    si.seek(2)
					    L=si.readlines()
					    for line in L:
					        print(line.strip())
					    #如果不移动数据流指针,想一次性读取所有数据流,可以使用getvalue()方法
					    print(si.getvalue())

					#程序输出
					29
					My website:
					www.qingsword.com
					29
					website:
					www.qingsword.com
					My website:
					www.qingsword.com

					
					#初始化一个二进制内存数据流对象,将晴刃这两个字编码成utf-8,写入这个数据流对象
					with BytesIO() as bf:
					    bf.write("晴刃".encode("utf-8"))
					    print(bf.getvalue())
					    bf.seek(0)
					    print(bf.read(3))
					    print(bf.tell())

					#程序输出
					b'\xe6\x99\xb4\xe5\x88\x83'
					b'\xe6\x99\xb4'
					3
					

必要时,我们可以将内存中的文件流写入磁盘中的指定文件中,例如:

					#!/usr/bin/env python3
					#coding=utf-8
					from io import StringIO
					#创建一个内存文件流,并传入两行字符串,同时打开一个可写的磁盘文件流
					with StringIO("晴刃\nwww.qingsword.com") as mf,\
					     open("qing","w") as f1:
					    mf.seek(0) #将内存文件流指针设置成0

					    #读取内存文件流中的所有数据,写入磁盘中当前脚本所在目录下的qing文件中
					    f1.write(mf.read())

					#查看写入是否成功
					with open("qing","r") as f1:
					    print(f1.read())
					
					#程序输出(输出换行了,因为内存流创建的时候,包含了一个换行符\n)
					晴刃
					www.qingsword.com