Python模块:shutil、序列化(json&pickle&shelve)、xml

时间:2023-03-08 16:13:28

shutil模块:

高级的 文件、文件夹、压缩包 处理模块

shutil.copyfileobj(fscr,fdst [, length])   # 将文件内容拷贝到另一个文件中

import shutil
f1 = open('import练习.py','r',encoding='utf-8')
f2 = open('import练习_new1.py','w',encoding='utf-8')
shutil.copyfileobj(f1,f2,length=1) # 处理结果:
# 将f1的内容复制到了f2中 # 因为 copyfileobj复制的文件中的内容, 所以两个文件应该先要 open

shutil.copyfile(src,dst)   # 拷贝文件

from shutil import *
copyfile('import练习.py','import练习new.py') # dst文件无需事先存在 # 处理结果:
# “import练习.py”这个文件复制了一个“import练习new.py” # copyfile是直接复制文件,处理的时候不涉及文件里面的内容

shutil.copymode(src, dst)   # 仅拷贝权限。内容、组、用户均不变

shutil.copystat(src, dst)   # 仅拷贝状态的信息,包括:mode bits, atime, mtime, flag

shutil.copy(src, dst)  # 拷贝文件和权限

shutil.copy2(src,dst)   # 拷贝文件和状态信息

递归的去拷贝文件夹:

shutil.ignore_patterns(*patterns)   #  忽略括号里面的内容(即一个或多个 patterns的内容)

shutil.copytree(scr,dst, ignore = shutil.ignore_patterns(*patterns)  )   #  把src文件夹复制成dst文件夹。 如果src文件夹里面有某些内容不想复制到dst文件夹中, 就利用 ignore = shutil.ignor_patterns(*patterns), 把不想复制的内容写到*patterns这个括号里面, 所以  这两个函数经常绑在一起使用。 如果copytree里面的ignore不写, 那就是把src文件夹中的所有内容都复制到dst文件夹中。

shutil.rmtree(path)   #  递归的去删除文件。 # 把这个路径下的所有内容都删掉   # 注意和os.removedirs(path)作对比,  os.removedirs(path)中的这个路径写的是最里层的路径。然后从最里层向上一层一层去删,而且 shutil.rmtree(path) 这个路径是删的顶层路径,就是说删的是这个路径下面的所有内容。

shutil.move(src,dst)  # 递归的去移动文件  # 如果dst中的文件夹真实存在,那就把src放到dst中那个真实存在的文件夹下面;如果dst中的那个文件夹名不存在,就把src移动到dst下面并把src的名称改为那个dst中原本不存在的文件夹名,即相当于移动src后又对src做了重命名。

shutil.make_archive(base_name,format,root_dir)   # 创建压缩包并返回文件路径

# base_name: 压缩包的文件名,也可以是压缩包的路径。如果是文件名,则保存至当前目录;如果是目录,则保存至指定路径(程序会把路径最后一个“ \ ”后面的内容当做是文件名)

# format: 压缩包种类:“zip”,“tar”,“bztar”,“gztar”

# root_dir:  要压缩的文件夹路径(默认当前目录)

序列化模块:(json&pickle和shelve)

往硬盘上存数据或者网络传输时,只接受bytes和字符串; 例如网络传输不支持字典、列表这类的形式。

概念: 把内存数据转成字符,叫序列化;

       把字符转成相应的内存数据类型,叫反序列化

json:

json的文件一般都以 .json 结尾,代表这里面的数据都是json序列化的, 而且 .json 的文件中不能出现单引号(变量要用双引号)

json的用法: json.dump( ), json.dumps(),json.load(),json.loads()

对于处理运行的py对象它自己,利用json.dumps()和json.loads()

json.dumps(obj)   # 把内存中的obj这个数据类型转化成字符串格式(由于处理的是py对象它自己,所以py中的数据类型读取到内存中的时候还是原先的数据类型,如py文件中的字典读取到内存后还是字典)

json.loads(str)    # 把内存中的要处理的这个字符串转换成非字符串类型(obj它原本的类型)

对于处理其他文件,利用json.dump() 和json.load()

json.dump(obj,fp)   # 把内存中的obj这个数据类型写入(write)后面的文件对象(fp)中。   注意: fp是文件对象,不能只写个文件名,所以dump之前要先以'w'的模式打开文件。

import json

info = {
'name':'neo',
'age':''
} f = open('json测试.json','w',encoding='utf-8') # dump之前要先以'w'模式open文件
json.dump(info,f)

json.load(fp)   # 把fp这个文件对象的内容读取到内存中并保留其在文件对象中原有的数据类型(即:内容在文件中是字典,读取到内存里面后仍然是字典这种数据类型)

import json
f = open('json测试.json','r',encoding='utf-8') # 以'r'的模式open文件 print(json.load(f)) # 同样的道理, load()里面写文件对象,不能只写一个文件名,load()之前你需要先open文件, load()不会自动把文件给你打开 # 输出结果:
# {'name': 'neo', 'age': '22'}

注意: 虽然多次往一个文件里dump()不会报错,但load()这个文件的时候就会报错(多次dump,文件里的内容会变成错误的数据类型)。 所以dump()时要一次性dump完所有的内容。

json.dumps() 和 json.loads() 只是在内存中进行字符串和其他数据类型的转换,它们存在的意义:

1. 网络传输; 由于网络传输只接受bytes和字符串(序列化的字符串也是bytes),所以可以利用它们把你的内存数据通过网络传输给别人

2. 定义了不同语言之间的交互规则。

附: 不同语言之间的交互规则有3种:

  • 纯文本; 坏处:不能共享复杂的数据类型
  • xml;坏处:占空间大
  • json, 简单,可读性好

pickle:

pickle的用法跟json大致相同。需要注意的是: pickle.dump()写入时候是现将obj的数据类型转成了二进制模式的bytes,所以写入时应该用“wb”,load()时应该用'rb'模式。

写入:

import pickle
d = { 'name':'neo','age':''}
f = open('pickle测试.pkl','wb') # 用pickle.dump()写文件的时候需要用“wb”模式, 因为pickle.dump()会先将写入的内容转换成二进制模式的bytes,而不是直接转换成字符串,所以写入的时候应该是二进制模式的写入。 # pickle的文件后缀写成 .pkl
print(pickle.dumps(d))
pickle.dump(d,f) # 处理结果:
# b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x03\x00\x00\x00neoq\x02X\x03\x00\x00\x00ageq\x03X\x02\x00\x00\x0022q\x04u.' # 二进制模式的bytes # pickle.dump()写入的文件如果以txt格式打开,会出现类似乱码的现象,这正常的,不是报错。 这种形式的“乱码”能够用pickle.load()正常读入内存。

读取到内存:

f = open('pickle测试.pkl','rb')   # 'rb' 模式读取
print(pickle.load(f)) # 输出结果:
# {'name': 'neo', 'age': '22'}

pickle能够dump多次、load多次,如下代码:

import pickle

data1 = ["a","bc"]
data2 = [1] f = open("pickle多次.pkl","wb") pickle.dump(data1,f)
pickle.dump(data2,f) for i in range(10):
pickle.dump(i,f) f.close() f = open("pickle多次.pkl","rb") try:
while True:
print(pickle.load(f))
except EOFError:
print("已读取完毕") f.close() # 运行结果:
# ['a', 'bc']
# [1]
#
#
#
#
#
#
#
#
#
#
# 已读取完毕

json vs pickle:

json:

json可序列化的数据类型只有有限的几种: str , int, tuple, list, dict (集合set都不支持)

优点: 可跨平台

pickle:

   pickle可序列化的数据类型支持Python里面的所有的数据类型(函数也可以)

   缺点:只能在Python里使用

shelve模块:

shelve模块是对pickle模块(注意:不是json)进行了封装,允许你dump()多次、load()多次

# 序列化
import shelve
f = shelve.open('shelve_test') # 'shelve_test'这个文件名不用加后缀, shelve会自动加上,不论是序列化还是反序列化; # 此时的f是一个k,v模式的数据类型,通过k能对v进行增删改查等操作,相当于一个字典,增删改查的方式跟字典的方式一样; # 对 f这个字典的v进行修改时, 要对v这个整体进行修改,而不能直接对v里面的某个部分直接进行修改(如: 假如v是一个列表:['a','b','c'], 不能这样修改:f[k][0]='A',而应该: f[k]=['A','b','c'] ,即要把v这个整体进行修改; 但查询的时候可以利用 “ f[k][2] ”这种方法) f['names'] = ['neo','alex','rain']
f['info'] = { 'name':'neo','age':22} # 利用这种方式对文件进持续化的操作, 这是往硬盘上写入,即序列化
f.close() # 反序列化
import shelve
f = shelve.open('shelve_test')
print(f['names']) # 这是从硬盘上读取,即反序列化

xml 模块:

xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单。至今很多传统公司如金融行业的很多系统的接口还主要是xml。

xml的主要格式如下,就是通过<>节点来区分数据结构的:

<?xml version="1.0"?>
<data >
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>

xml协议在各个语言里都是支持的,在Python中可以使用以下模块操作xml:

import xml.etree.ElementTree as ET  # xml.etree.ElementTree是操作xml的模块
tree = ET.parse('test1.xml') # 读取xml文件, 相当于 f = open
root = tree.getroot() # 得到根“目录”,相当于 f seek(0)
print(root.tag) # 打印root的< >里面的标签(如:data) # 遍历xml文档
for child in root: # 在根“目录”里面循环它的下一级(如: country)
print(child.tag,child.attrib) # 打印child的< > 里面的标签和属性,属性(attrib)打印出来是一个字典的形式
for i in child: # 循环child的下一级i
print(i.tag,i.text) # 打印i里面的标签和i里面的具体内容 # 打印结果:
# data # 打印的root的tag
# country {'name': 'Liechtenstein'} # .attrib是一个字典的形式
# rank 2
# year 2008
# gdppc 141100
# neighbor None
# neighbor None
# country {'name': 'Singapore'}
# rank 5
# year 2011
# gdppc 59900
# neighbor None
# country {'name': 'Panama'}
# rank 69
# year 2011
# gdppc 13600
# neighbor None
# neighbor None # 只遍历 year 节点 for node in root.iter('year'): # root.iter('year') 把root中所有的‘year’ 这一行,变成可迭代
print(node.tag,node.text) # 打印结果:
# year 2008
# year 2011
# year 2011

修改和删除xml文档内容:

# 修改:

#  修改:

import xml.etree.ElementTree as ET

tree = ET.parse('test1.xml')
root = tree.getroot() # 修改‘year’这个节点
for node in root.iter('year'):
new_year = int(node.text)+1 # 文档中的‘year’是字符串格式,需要先变成int格式
node.text = str(new_year) # 修改完之后还需要改成字符串格式, 因为只有字符串格式才能写入xml
node.set('updated or not','yes') # 这个语法是添加 attrib
tree.write('test2.xml') # write() 写入的文档名可以是当前文档本身,也可以是写入一个新文档里面

修改完的xml如下:

<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year updated or not="yes">2009</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year updated or not="yes">2012</year>
<gdppc>59900</gdppc>
<neighbor direction="N" name="Malaysia" />
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year updated or not="yes">2012</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>

删除:

删除1: 这个是我自己写的:

#  删除gdppc小于13700 的 country:

import xml.etree.ElementTree as ET

tree = ET.parse('test1.xml')
root = tree.getroot() for node in root.iter('country'): # root.iter('country) :先让‘country’这一栏变成可迭代; node是其中的一个‘country’
for i in node.iter('gdppc'): # 再让node中的'gdppc' 变成可迭代; i是其中一个 gdppc
if int(i.text) < 13700:
root.remove(node) # root.remove(): 删除这个node(‘country’)
tree.write('text3.xml')

删除后的xml如下:

<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor direction="N" name="Malaysia" />
</country>
</data>

删除2:示例写的:

#  删除rank大于50的 country

import xml.etree.ElementTree as ET

tree = ET.parse('test1.xml')
root = tree.getroot() for i in root.findall('country'): # 利用 .findall('country') 找到所有的‘country’这一栏; i 是其中一个‘country’
rank = int(i.find('rank').text) # i.find('rank').text 找到i中rank这一行,然后再找到rank这一行的text
if rank > 50:
root.remove(i) # 删除这个i (‘country’) tree.write('test4.xml')

删除完的xml如下:

<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor direction="N" name="Malaysia" />
</country>
</data>

xml 创建:

直接上代码:

import xml.etree.ElementTree as ET

new_xml = ET.Element("namelist")   # 创建一个xml对象; ‘namelist’就是新xml的根
name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"}) # 往new_xml 下放一个‘name’的节点,它的属性(attrib)就是 {"enrolled":"yes"},可以放多个属性(attrib)
name.text = 'neo'
age = ET.SubElement(name,"age",attrib={"checked":"no"}) # 再往name这个节点下放入‘age’这个节点
sex = ET.SubElement(name,"sex") # 往name这个节点下放入‘sex’这个节点
sex.text = 'male' # ‘sex’这个节点下放入具体的参数(text) # 创建第二个放在new_text下的name2节点
name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"})
age = ET.SubElement(name2,"age")
age.text = '' # ‘age’节点下放入具体的参数(text) et = ET.ElementTree(new_xml) #生成文档对象 # 节点组织好后开始存文件
et.write("test.xml", encoding="utf-8",xml_declaration=True) # write()里面的文件名一定要写 ET.dump(new_xml) #打印生成的格式 # This function should be used for debugging only. # 打印结果:
# <namelist><name enrolled="yes">neo<Age checked="no" /><Sex>33</Sex></name><name enrolled="no"><Age>19</Age></name></namelist>

创建的xml如下:

<?xml version='1.0' encoding='utf-8'?>   #  这句是由 write()中的'xml_declaration=True' 写入的
<namelist><name enrolled="yes">neo<Age checked="no" /><Sex>33</Sex></name><name enrolled="no"><Age>19</Age></name></namelist>

格式化后的效果如下:

<?xml version='1.0' encoding='utf-8'?>
<namelist>
<name enrolled="yes">
neo
<Age checked="no" />
<Sex>33</Sex>
</name>
<name enrolled="no">
<Age>19</Age>
</name>
</namelist>