python基础之文件
概要:
- 文件操作
- 文件夹和路径
- csv格式文件
- ini格式文件
- xml格式文件
- excel文件
- 压缩文件
注意:每种格式包含很多相关操作,大家在学习的过程中只要掌握知识点的用法,参考笔记可以实现相关的练习即可,不必背会(在企业开发过程中也是边搜边实现。
1. 文件操作
- 字符串类型str
- 字节类型 bytes
- 表示文字信息,本质上是utf-8 或 gbk等编码的二进制(对unicode进行压缩,方便文件存储和网络过传输)
- 可表示原始二进制(图片、文件等信息)
1.1 读文件
- 路径:绝对路径 和 相对路径
- 模式:rb,read,binary,写入内容必须是bytes类型;rt:read,text,写入字符串类型。
- 判断文件是否存在:os.path.exists(r'c:\new\file.txt')
- f = open('file.txt', mode='rb')
- f = open('file.txt', mode='rt', encoding='utf-8')
- f.read()
- f.close()
实质上文件本身内容都是二进制形式,文本文件、图片、视频、音频等不过是编码标准不同罢了,之所以文本文件可以rt,是因为python解释器open函数做了自动解码的操作。
【坑1】
- 1.路径:绝对路径和相对路径要分清楚;
- 2.windows系统路径字符串问题,\ 容易导致转义字符出现,路径解析错误,解决方案:
- 路径中反斜杠使用双反斜杠 \\
- 使用r前缀字符串。例如:r'c:\new\file.txt'
- 3.文件可能不存在,会报错,因此先判断:
os.path.exists(r'c:\new\file.txt')
1.2 写文件
步骤:
- 使用wb模式:
- 打开文件:f = open('file.txt', mode='wb')
- 写入文件:f.write('中国人'.encode('utf-8')) .wb模式必须写入bytes类型,是直接写二进制值,不需要定义编码格式
- 关闭文件:f.close()
- 使用wt模式:
- 打开文件:f = open('file.txt', mode='wt', encoding='utf-8')
- 写入文件:f.write('中国人') wt模式写入字符串类型
- 关闭文件:f.close()
- 图片等二进制文件:
直接使用wb模式写入,因为是直接写入二进制值,因此不需要定义编码格式。
注意:w模式,如果文件不存在,会自动创建一个。如果文件存在,先清空文件,再写入内容。
【小高级案例:(超前)】
利用Python向某个网址发送请求并获取结果(利用第三方的模块)
- 下载第三方模块
- pip install requests
- /Library/Frameworks/Python.framework/Versions/3.9/bin/pip3 install requests
- 使用第三方模块
import requests
res = requests.get(url="网址")
print(res)
1.3 文件打开模式
- b、t模式实际上读取的都是二进制值,只不过t模式python会自动根据你提供的编码形式,自动转换为字符串格式。
- r、rt是一样的,写成r,默认就是rt;
打开模式常见有:
关于文件的打开模式常见应用有:
-
只读:
r
、rt
、rb
(用)- 存在,读
- 不存在,报错
-
只写:
w
、wt
、wb
(用)- 存在,清空再写
- 不存在,创建再写
-
只写:
x
、xt
、xb
- 存在,报错
- 不存在,创建再写。
-
只写:
a
、at
、ab
【尾部追加】(用)- 存在,尾部追加。
- 不存在,创建再写。
-
读写
- r+、rt+、rb+,默认光标位置:起始位置
- w+、wt+、wb+,默认光标位置:起始位置(清空文件)
- x+、xt+、xb+,默认光标位置:起始位置(新文件)
- a+、at+、ab+,默认光标位置:末尾
【坑2】
注意每次读写完毕,光标所在的位置,否则可能出现无法预料的结果。
f.seek(0) # 将光标回到起始位置
1.4 常见功能
- read,读 【常用,小文件一次读取】
- 读所有 f.read() # 不加参数缺陷:如果文件过大,有可能内存不足,程序崩溃;
- t模式读n个字符(b模式读n个字节) f.read(n),再次强调,size 表示的是一次最多可读取的字符(或字节)数,因此,即便设置的 size 大于文件中存储的字符(字节)数,read() 函数也不会报错,它只会读取文件中所有的数据。
- readline,读一行数据 f.readline() # 相对节省内存。【由于 readline() 函数在读取文件中一行的内容时,会读取最后的换行符“\n”,再加上 print() 函数输出内容时默认会换行,所以输出结果中会看到多出了一个空行。】
- readlines,读出所有行,每行作为列表的一个元素
【注意】当文件过大时,不知道有多少行,又想读出文件所有内容,可以使用for循环实现:
f = open('file.txt', mode='rt', encoding='utf-8')
for line in f:
print(line)
- write,写。
- flush,刷到硬盘。其实write操作并不是直接写入硬盘,而是写入缓冲区,因此,write执行后,立刻查看文件内容,不一定有刚刚写入的内容,因为此时可能数据还在缓冲区。flush 的作用是发送指令让操作系统将缓冲区的内容立刻写到硬盘。 f.flush()
- seek(n),移动光标位置(字节)。 【注意】无论是什么模式打开的文件,都是以字节为单位移动光标。 f.seek(n):光标向后移动3个字节。【注意:在a模式下,调用write在文件中写入内容时,永远只能将内容写入到尾部,不会写到光标的位置。】【如果移动光标位置不对,可能会出现乱码,因为非英文字符,utf-8是多个字节编码,如果不是编码字节数的整数倍,可能会在字符中间截断,因此出现解码乱码的情况。】
file.seek(off, whence=0):
从文件中移动off个操作标记(文件指针),off参数必须传递,正值往结束方向移动,负值往开始方向移动。如果设定了whence参数,就以whence设定的起始位为准,0代表从头开始,1代表当前位置,2代表文件最末尾位置。
【坑3】seek(off, whence)【注意,当 offset 值非 0 时,Python 要求文件必须要以二进制格式打开,否则会抛出 io.UnsupportedOperation 错误。】
使用seek()方法报错:“io.UnsupportedOperation: can't do nonzero cur-relative seeks”错误的原因:
在使用seek()函数时,有时候会报错为 “io.UnsupportedOperation: can't do nonzero cur-relative seeks”,代码如下:
>>> f=open("aaa.txt","r+") #以读写的格式打开文件aaa.txt
>>> f.read() #读取文件内容
'my name is liuxiang,i am come frome china'
>>> f.seek(3,0) #从开头开始偏移三个单位(偏移到“n”)
3
>>> f.seek(5,1) #想要从上一次偏移到的位置(即“n”)再偏移5个单位
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
io.UnsupportedOperation: can't do nonzero cur-relative seeks
照理说,按照seek()方法的格式file.seek(offset,whence),后面的1代表从当前位置开始算起进行偏移,那又为什么报错呢?
这是因为,在文本文件中,没有使用b模式选项打开的文件,只允许从文件头开始计算相对位置,从文件尾计算时就会引发异常。将 f=open("aaa.txt","r+") 改成
f = open("aaa.txt","rb") 就可以了:
>>> f = open("aaa.txt","rb")
>>> f.seek(3,0)
3
>>> f.seek(5,1)
8
【例外:当seek的offset值为0时,t、b模式打开都行,且whence非0也不报错,因为光标只相当于移动到特定位置,此时seek(0)即回到文件开头,seek(0, 2)即跳到文件末尾。】
- tell,获取当前光标的位置。 f.tell()
- close,关闭文件。
【另外,在写入文件完成后,一定要调用 close() 函数将打开的文件关闭,否则写入的内容不会保存到文件中。例如,将上面程序中最后一行 f.close() 删掉,再次运行此程序并打开 a.txt,你会发现该文件是空的。这是因为,当我们在写入文件内容时,操作系统不会立刻把数据写入磁盘,而是先缓存起来,只有调用 close() 函数时,操作系统才会保证把没有写入的数据全部写入磁盘文件中。除此之外,如果向文件写入数据后,不想马上关闭文件,也可以调用文件对象提供的 flush() 函数,它可以实现将缓冲区的数据写入文件中。】
【补充:文件属性】
- f.mode:文件打开模式
- f.encoding:编码格式
- f.name:文件名
- f.closed:是否关闭
【补充:文件其他方法】
- writelines(lines):写入多行,lines是一个列表或元组,将所有元素拼接后生成的字符串写入,元素之间不加换行符及其他字符。
- fileno():该函数用于得到文件在进程中的编号,这是一个整数值。其中,stdin 在进程中的文件编号永远是 0,stdout 永远是 1,stderr 永远是 2,其他文件的编号都大于 2。如果该文件已经被关闭,则 fileno() 会抛出 ValueError 异常。
- f.isatty():检测文件是否连接到一个终端设备,如果是返回 True,否则返回 False。
- f.readable():检查文件是否可读,可读返回True,否则返回False。
- f.writeable():检查文件是否可写,可写返回 True,否则返回 False。
- f.seekable():如果文件是可查找的,seekable()则返回True,否则返回False。如果文件允许访问文件流,则该文件是可查找的,例如,seek()方法。
- f.truncate(n):方法将文件大小截断为给定的字节数,后面的内容全部删除。如果未指定大小,将使用当前位置。【具体使用参看自己总结13】
【坑4】
open()的文本格式和二进制格式
使用 open() 函数以文本格式打开文件和以二进制格式打开文件,唯一的区别是对文件中换行符的处理不同。
在 Windows 系统中,文件中用 "\r\n" 作为行末标识符(即换行符),当以文本格式读取文件时,会将 "\r\n" 转换成 "\n";反之,以文本格式将数据写入文件时,会将 "\n" 转换成 "\r\n"。这种隐式转换换行符的行为,对用文本格式打开文本文件是没有问题的,但如果用文本格式打开二进制文件,就有可能改变文本中的数据(将 \r\n 隐式转换为 \n)。
而在 Unix/Linux 系统中,默认的文件换行符就是 \n,因此在 Unix/Linux 系统中文本格式和二进制格式并无本质的区别。
总的来说,为了保险起见,对于 Windows平台最好用 b 打开二进制文件;对于 Unix/Linux 平台,打开二进制文件,可以用 b,也可以不用。
【补充】同时打开多个文件写法: with open('1.txt', mode='rb') as f1, open('2.txt', mode='rb') as f2, ... :
1.5 上下文管理 with
之前对文件进行操作时,每次都要打开和关闭文件,比较繁琐且容易忘记关闭文件。
以后再进行文件操作时,推荐大家使用with上下文管理,它可以自动实现关闭文件。
with open('file.txt', mode='rb') as f:
data = f.read()
print(data)
python2.7 后,with支持同时打开多个文件,对多个文件的上下文管理,即:
with open("xxxx.txt", mode='rb') as f1, open("xxxx.txt", mode='rb') as f2:
pass
2.csv格式文件
逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。
对于这种格式的数据,我们需要利用open函数来读取文件并根据逗号分隔的特点来进行处理。
3.ini格式文件
ini文件是Initialization File的缩写,平时用于存储软件的的配置文件。例如:MySQL数据库的配置文件。
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-bin=py-mysql-bin
character-set-server=utf8
collation-server=utf8_general_ci
log-error=/var/log/mysqld.log
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
[client]
default-character-set=utf8
configparser 模块
config = configparser.ConfigParser()
config.read('files/my.ini', encoding='utf-8')
- 1.获取所有的节点 config.sections()
- 2.获取节点下的键值 config.items("mysqld_safe")
- 3.获取某个节点下的键对应的值 config.get("mysqld","collation-server")
- 4.是否存在节点 config.has_section("client")
- 5.添加一个节点
import configparser
config = configparser.ConfigParser()
config.read('files/my.ini', encoding='utf-8')
# config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.ini', encoding='utf-8')
# 1.获取所有的节点
result = config.sections()
print(result) # ['mysqld', 'mysqld_safe', 'client']
# 2.获取节点下的键值
result = config.items("mysqld_safe")
print(result) # [('log-error', '/var/log/mariadb/mariadb.log'), ('pid-file', '/var/run/mariadb/mariadb.pid')]
for key, value in config.items("mysqld_safe"):
print(key, value)
# 3.获取某个节点下的键对应的值
result = config.get("mysqld","collation-server")
print(result)
# 4.其他
# 4.1 是否存在节点
v1 = config.has_section("client")
print(v1)
# 4.2 添加一个节点
config.add_section("group")
config.set('group','name','wupeiqi')
config.set('client','name','wupeiqi')
config.write(open('files/new.ini', mode='w', encoding='utf-8'))
# 4.3 删除
config.remove_section('client')
config.remove_option("mysqld", "datadir")
config.write(open('files/new.ini', mode='w', encoding='utf-8'))
-
读取所有节点
import configparser config = configparser.ConfigParser() config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.conf', encoding='utf-8') # config.read('my.conf', encoding='utf-8') ret = config.sections() print(ret) >>输出 ['mysqld', 'mysqld_safe', 'client']
-
读取节点下的键值
import configparser config = configparser.ConfigParser() config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.conf', encoding='utf-8') # config.read('my.conf', encoding='utf-8') item_list = config.items("mysqld_safe") print(item_list) >>输出 [('log-error', '/var/log/mariadb/mariadb.log'), ('pid-file', '/var/run/mariadb/mariadb.pid')]
-
读取节点下值(根据 节点+键 )
import configparser config = configparser.ConfigParser() config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.conf', encoding='utf-8') value = config.get('mysqld', 'log-bin') print(value) >>输出 py-mysql-bin
-
检查、删除、添加节点
import configparser config = configparser.ConfigParser() config.read('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/my.conf', encoding='utf-8') # config.read('my.conf', encoding='utf-8') # 检查 has_sec = config.has_section('mysqld') print(has_sec) # 添加节点 config.add_section("SEC_1") # 节点中设置键值 config.set('SEC_1', 'k10', "123") config.set('SEC_1', 'name', "哈哈哈哈哈") config.add_section("SEC_2") config.set('SEC_2', 'k10', "123") # 内容写入新文件 config.write(open('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/xxoo.conf', 'w')) # 删除节点 config.remove_section("SEC_2") # 删除节点中的键值 config.remove_option('SEC_1', 'k10') config.write(open('/Users/wupeiqi/PycharmProjects/luffyCourse/day09/files/new.conf', 'w'))
4.XML格式文件
可扩展标记语言,是一种简单的数据存储语言,XML 被设计用来传输和存储数据。
- 存储,可用来存放配置文件,例如:java的配置文件。
- 传输,网络传输时以这种格式存在,例如:早期ajax传输的数据、soap协议等。
java用的较多,python使用较少,不过后面微信小程序会用到。
【看笔记,随后汇总】
4.1 读取xml文件内容
from xml.etree import ElementTree as ET
# ET去打开xml文件
tree = ET.parse("files/xo.xml")
# 获取根标签
root = tree.getroot()
print(root) # <Element 'data' at 0x7f94e02763b0>
读取xml格式字符串内容
from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
root = ET.XML(content)
print(root) # <Element 'data' at 0x7fdaa019cea0>
4.2 读取节点数据
(1)通过find函数获取节点:
from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein" id="999" >
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
# 获取根标签 data
root = ET.XML(content)
# 获取第一个名字为country的标签
country_object = root.find("country")
# .tag 是标签的名称,这里是country;
# .attrib 是标签的属性,这里是 name="Liechtenstein"
# .text 是标签里面的内容,这个标签没有
print(country_object.tag, country_object.attrib)
gdppc_object = country_object.find("gdppc")
# tag=gdppc, attrib={}, text=141100
print(gdppc_object.tag,gdppc_object.attrib,gdppc_object.text)
(2)通过for in循环获取子节点:
from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
# 获取根标签 data
root = ET.XML(content)
# 获取data标签的孩子标签
for child in root:
# child.tag = conntry
# child.attrib = {"name":"Liechtenstein"}
print(child.tag, child.attrib)
for node in child:
print(node.tag, node.attrib, node.text)
(3)通过生成迭代器获取子节点:
from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
root = ET.XML(content)
# 找到所有的year标签,生成迭代器
for child in root.iter('year'):
print(child.tag, child.text)
(4) 通过findall、find函数获取子节点:
from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
root = ET.XML(content)
# findall找到所有的country标签
v1 = root.findall('country')
print(v1)
# find 找到第一个country标签,然后再找到这个标签下面的第一个rank标签
v2 = root.find('country').find('rank')
# 通过 .text获取rank标签的内容,这里为:2
print(v2.text)
4.3 修改和删除节点
from xml.etree import ElementTree as ET
content = """
<data>
<country name="Liechtenstein">
<rank>2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Panama">
<rank>69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>
"""
root = ET.XML(content)
# 修改节点内容和属性
rank = root.find('country').find('rank')
print(rank.text)
# .text= 将rank标签内容修改为‘999’
rank.text = "999"
# .set() 给rank添加一个属性update,并设置值为'2020-11-11'
rank.set('update', '2020-11-11')
print(rank.text, rank.attrib)
############ 保存文件 ############
tree = ET.ElementTree(root)
tree.write("new.xml", encoding='utf-8')
# 删除节点,下面代码删除掉了第一个country节点及其下面所有子节点
root.remove( root.find('country') )
print(root.findall('country'))
############ 保存文件 ############
tree = ET.ElementTree(root)
tree.write("newnew.xml", encoding='utf-8')
【注意:xml中所有属性的值和标签的内容都是字符串类型,赋值其他类型会报错。】
4.4 构建文档
方式一(ET.Element() 方法,这种方式看起来简单明了):
<home>
<son name="儿1">
<grandson name="儿11"></grandson>
<grandson name="儿12"></grandson>
</son>
<son name="儿2"></son>
</home>
from xml.etree import ElementTree as ET
# 创建根标签
root = ET.Element("home")
# 创建节点大儿子
son1 = ET.Element('son', {'name': '儿1'})
# 创建小儿子
son2 = ET.Element('son', {"name": '儿2'})
# 在大儿子中创建两个孙子
grandson1 = ET.Element('grandson', {'name': '儿11'})
grandson2 = ET.Element('grandson', {'name': '儿12'})
son1.append(grandson1)
son1.append(grandson2)
# 把儿子添加到根节点中
root.append(son1)
root.append(son2)
tree = ET.ElementTree(root)
# short_empty_elements 是否使用短标签,如果一个标签里面text为空,
# 可以使用<son name='alex' /> 形式保存,而不需要后面加</son>,这就是短标签,
# 相当于简写,这里设置值为False,则使用完整格式标签。
tree.write('oooo.xml', encoding='utf-8', short_empty_elements=False)
方式二(某标签.makeelement() 方法,该方法可以使用现有的某个标签创建子标签,跟Element() 方法同等效果):
<famliy>
<son name="儿1">
<grandson name="儿11"></grandson>
<grandson name="儿12"></grandson>
</son>
<son name="儿2"></son>
</famliy>
from xml.etree import ElementTree as ET
# 创建根节点
root = ET.Element("famliy")
# 创建大儿子
son1 = root.makeelement('son', {'name': '儿1'})
# 创建小儿子
son2 = root.makeelement('son', {"name": '儿2'})
# 在大儿子中创建两个孙子
grandson1 = son1.makeelement('grandson', {'name': '儿11'})
grandson2 = son1.makeelement('grandson', {'name': '儿12'})
son1.append(grandson1)
son1.append(grandson2)
# 把儿子添加到根节点中
root.append(son1)
root.append(son2)
tree = ET.ElementTree(root)
tree.write('oooo.xml',encoding='utf-8')
方式三(ET.SubElement() 方法,最省事的方式):
<famliy>
<son name="儿1">
<age name="儿11">孙子</age>
</son>
<son name="儿2"></son>
</famliy>
from xml.etree import ElementTree as ET
# 创建根节点
root = ET.Element("famliy")
# 创建节点大儿子
son1 = ET.SubElement(root, "son", attrib={'name': '儿1'})
# 创建小儿子
son2 = ET.SubElement(root, "son", attrib={"name": "儿2"})
# 在大儿子中创建一个孙子
grandson1 = ET.SubElement(son1, "age", attrib={'name': '儿11'})
grandson1.text = '孙子'
et = ET.ElementTree(root) #生成文档对象
et.write("test.xml", encoding="utf-8")
方式四(通过修改.text属性直接赋值):
<user><![CDATA[你好呀]]</user>
from xml.etree import ElementTree as ET
# 创建根节点
root = ET.Element("user")
root.text = "<![CDATA[你好呀]]"
et = ET.ElementTree(root) # 生成文档对象
et.write("test.xml", encoding="utf-8")
【案例 解析xml文件】
content = """<xml>
<ToUserName><![CDATA[gh_7f083739789a]]></ToUserName>
<FromUserName><![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8]]></FromUserName>
<CreateTime>1395658920</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[TEMPLATESENDJOBFINISH]]></Event>
<MsgID>200163836</MsgID>
<Status><![CDATA[success]]></Status>
</xml>"""
#####
from xml.etree import ElementTree as ET
info = {}
root = ET.XML(content)
for node in root:
# print(node.tag,node.text)
# 因为CDATA是xml内部一种固定搭配,所以解析时会自动把里面的数据提取出来;
info[node.tag] = node.text
print(info)
5.Excel格式文件 【openpyxl 模块】
安装第三方模块:
pip install openpyxl
5.1 读Excel
(1)读sheet
from openpyxl import load_workbook
wb = load_workbook("files/p1.xlsx")
# sheet相关操作
# 1.获取excel文件中的所有sheet名称
print(wb.sheetnames) # ['数据导出', '用户列表', 'Sheet1', 'Sheet2']
# 2.选择sheet,基于sheet名称
sheet = wb["数据导出"]
# .cell(1, 2) 获取第一行,第二列那个单元格的对象
cell = sheet.cell(1, 2)
# .value 获取该单元格的值
print(cell.value)
# 3.选择sheet,基于索引位置
sheet = wb.worksheets[0]
cell = sheet.cell(1,2)
print(cell.value)
# 4.循环所有的sheet
# 第一种方式:
for name in wb.sheetnames:
sheet = wb[name]
cell = sheet.cell(1, 1)
print(cell.value)
# 第二种方式:
for sheet in wb.worksheets:
cell = sheet.cell(1, 1)
print(cell.value)
# 第三种方式(本质上是执行的第二种方式):
for sheet in wb:
cell = sheet.cell(1, 1)
print(cell.value)
(2)读sheet中单元格的值:
from openpyxl import load_workbook
wb = load_workbook("files/p1.xlsx")
sheet = wb.worksheets[0]
# 1.获取第N行第N列的单元格(行、列位置是从1开始)
cell = sheet.cell(1, 1)
# .value 单元格的值
print(cell.value)
# .style 样式
print(cell.style)
# .font 字体信息
print(cell.font)
print(cell.alignment)
# 2.获取某个单元格
c1 = sheet["A2"]
print(c1.value)
c2 = sheet['D4']
print(c2.value)
# 3.第N行所有的单元格
# sheet[n] : 获取第n行的所有单元格
for cell in sheet[1]:
print(cell.value)
# 4.所有行的数据(获取某一列数据)
# sheet.rows,获取所有每一行的单元格
for row in sheet.rows:
# row[0],第一列单元格的值,row[1],第二列单元格的值
print(row[0].value, row[1].value)
# 5.获取所有列的数据
for col in sheet.columns:
# col[n],获取第n行的素有数据
print(col[1].value)
(3)读取合并的单元格
excel合并单元格的值是取的第一个值,水平方向取最左侧单元格的值,垂直方向取最上方的单元格的值。因此第一个单元格的类型仍为<cell...>,被合并的单元格类型则为<MergeCell ...>,注意下方输出结果中各个单元格的类型。例如下图,用户信息实际是A1的值,B1因为被合并,所以B1的值为空,所以获取的值为None。
from openpyxl import load_workbook
wb = load_workbook("files/p1.xlsx")
sheet = wb.worksheets[2]
# 获取第N行第N列的单元格(位置是从1开始)
c1 = sheet.cell(1, 1)
print(c1) # <Cell 'Sheet1'.A1>
print(c1.value) # 用户信息
c2 = sheet.cell(1, 2)
print(c2) # <MergedCell 'Sheet1'.B1>
print(c2.value) # None
from openpyxl import load_workbook
wb = load_workbook('files/p1.xlsx')
sheet = wb.worksheets[2]
for row in sheet.rows:
print(row)
>>> 输出结果
(<Cell 'Sheet1'.A1>, <MergedCell 'Sheet1'.B1>, <Cell 'Sheet1'.C1>)
(<Cell 'Sheet1'.A2>, <Cell 'Sheet1'.B2>, <Cell 'Sheet1'.C2>)
(<Cell 'Sheet1'.A3>, <Cell 'Sheet1'.B3>, <Cell 'Sheet1'.C3>)
(<MergedCell 'Sheet1'.A4>, <Cell 'Sheet1'.B4>, <Cell 'Sheet1'.C4>)
(<Cell 'Sheet1'.A5>, <Cell 'Sheet1'.B5>, <Cell 'Sheet1'.C5>)
5.1 写Excel
(1)在原Excel文件基础上写内容。(获取cell,使用cell.value='alex'写内容。)
from openpyxl import load_workbook
wb = load_workbook('files/p1.xlsx')
sheet = wb.worksheets[0]
# 找到单元格,并修改单元格的内容
cell = sheet.cell(1, 1)
cell.value = "新的开始"
# 将excel文件保存到p2.xlsx文件中
wb.save("files/p2.xlsx")
(2)新创建Excel文件写内容。(写值方式跟上面相同。)
from openpyxl import workbook
# 创建excel且默认会创建一个sheet(名称为Sheet)
wb = workbook.Workbook()
sheet = wb.worksheets[0] # 或 sheet = wb["Sheet"]
# 找到单元格,并修改单元格的内容
cell = sheet.cell(1, 1)
cell.value = "新的开始"
# 将excel文件保存到p2.xlsx文件中
wb.save("files/p2.xlsx")
(3)其他操作:
from openpyxl import workbook
wb = workbook.Workbook() # Sheet
# 1. 修改sheet名称(sheet.title='数据集')
sheet = wb.worksheets[0]
sheet.title = "数据集"
wb.save("p2.xlsx")
# 2. 创建sheet并设置sheet颜色
# 在0位置创建一个sheet,命名为“工作计划”
sheet = wb.create_sheet("工作计划", 0)
# 给sheet设置一个颜色
sheet.sheet_properties.tabColor = "1072BA"
wb.save("p2.xlsx")
# 3. 默认打开的sheet(.active = 0)
wb.active = 0
wb.save("p2.xlsx")
# 4. 拷贝sheet(wb.copy_worksheet('alex'))
sheet = wb.create_sheet("工作计划")
sheet.sheet_properties.tabColor = "1072BA"
new_sheet = wb.copy_worksheet(wb["Sheet"])
new_sheet.title = "新的计划"
wb.save("p2.xlsx")
# 5.删除sheet
del wb["用户列表"]
wb.save('files/p2.xlsx')
from openpyxl import load_workbook
from openpyxl.styles import Alignment, Border, Side, Font, PatternFill, GradientFill
wb = load_workbook('files/p1.xlsx')
sheet = wb.worksheets[1]
# 1. 获取某个单元格,修改值(1)
cell = sheet.cell(1, 1)
cell.value = "开始"
wb.save("p2.xlsx")
# 2. 获取某个单元格,修改值(2)
sheet["B3"] = "Alex"
wb.save("p2.xlsx")
# 3. 获取某些单元格,修改值(3)
# 类似切片,但是excel中是切块。如下图,实际相当于如下的元组嵌套
(
(B2,C2)
(B3,C3)
)
# 下面的循环相当于上面元组的嵌套循环取值:
cell_list = sheet["B2":"C3"]
for row in cell_list:
for cell in row:
cell.value = "新的值"
wb.save("p2.xlsx")
# 4. 对齐方式
cell = sheet.cell(1, 1)
# horizontal,水平方向对齐方式:"general", "left", "center", "right", "fill", "justify", "centerContinuous", "distributed"
# vertical,垂直方向对齐方式:"top", "center", "bottom", "justify", "distributed"
# text_rotation,旋转角度。
# wrap_text,是否自动换行。
cell.alignment = Alignment(horizontal='center', vertical='distributed', text_rotation=45, wrap_text=True)
wb.save("p2.xlsx")
# 5. 边框(注意得事先导入模块)
# side的style有如下:dashDot','dashDotDot', 'dashed','dotted','double','hair', 'medium', 'mediumDashDot', 'mediumDashDotDot','mediumDashed', 'slantDashDot', 'thick', 'thin'
cell = sheet.cell(9, 2)
cell.border = Border(
top=Side(style="thin", color="FFB6C1"),
bottom=Side(style="dashed", color="FFB6C1"),
left=Side(style="dashed", color="FFB6C1"),
right=Side(style="dashed", color="9932CC"),
diagonal=Side(style="thin", color="483D8B"), # 对角线
diagonalUp=True, # 左下 ~ 右上
diagonalDown=True # 左上 ~ 右下
)
wb.save("p2.xlsx")
# 6.字体
cell = sheet.cell(5, 1)
cell.font = Font(name="微软雅黑", size=45, color="ff0000", underline="single")
wb.save("p2.xlsx")
# 7.背景色
cell = sheet.cell(5, 3)
cell.fill = PatternFill("solid", fgColor="99ccff")
wb.save("p2.xlsx")
# 8.渐变背景色
cell = sheet.cell(5, 5)
cell.fill = GradientFill("linear", stop=("FFFFFF", "99ccff", "000000"))
wb.save("p2.xlsx")
# 9.宽高(索引从1开始)
sheet.row_dimensions[1].height = 50
sheet.column_dimensions["E"].width = 100
wb.save("p2.xlsx")
# 10.合并单元格
# 合并单元格
# B2至D8单元块合并
sheet.merge_cells("B2:D8")
# 第二种方式合并单元格
sheet.merge_cells(start_row=15, start_column=3, end_row=18, end_column=8)
wb.save("p2.xlsx")
#取消合并单元格
sheet.unmerge_cells("B2:D8")
wb.save("p2.xlsx")
# 11.写入公式
sheet = wb.worksheets[3]
sheet["D1"] = "合计"
# 写入公式
sheet["D2"] = "=B2*C2"
wb.save("p2.xlsx")
sheet = wb.worksheets[3]
# 还可以写入内置函数
sheet["D3"] = "=SUM(B3,C3)"
wb.save("p2.xlsx")
# 12.删除
# idx,要删除的索引位置
# amount,从索引位置开始要删除的个数(默认为1)
sheet.delete_rows(idx=1, amount=20)
sheet.delete_cols(idx=1, amount=3)
wb.save("p2.xlsx")
# 13.插入
sheet.insert_rows(idx=5, amount=10)
sheet.insert_cols(idx=3, amount=2)
wb.save("p2.xlsx")
# 14.循环写内容
sheet = wb["Sheet"]
cell_range = sheet['A1:C2']
for row in cell_range:
for cell in row:
cell.value = "xx"
for row in sheet.iter_rows(min_row=5, min_col=1, max_col=7, max_row=10):
for cell in row:
cell.value = "oo"
wb.save("p2.xlsx")
# 15.移动
# 将H2:J10范围的数据,向右移动15个位置、向下移动1个位置(如果负值,则向左、向上移动)
sheet.move_range("H2:J10",rows=1, cols=15)
wb.save("p2.xlsx")
sheet = wb.worksheets[3]
sheet["D1"] = "合计"
sheet["D2"] = "=B2*C2"
sheet["D3"] = "=SUM(B3,C3)"
sheet.move_range("B1:D3",cols=10, translate=True) # 移动后,原来的公式自动变更新位置坐标,自动翻译公式
wb.save("p2.xlsx")
# 16.打印区域
sheet.print_area = "A1:D200"
wb.save("p2.xlsx")
# 17.打印时,每个页面的固定表头
# 相当于保留A列到D列的第一行作为每张打印页的表头
sheet.print_title_cols = "A:D"
sheet.print_title_rows = "1:1"
wb.save("p2.xlsx")
6.压缩文件(shutil 模块)
基于python内置的shutil模块可以实现对压缩文件的操作。
import shutil
# 1. 压缩文件
# base_name,压缩后的压缩包文件
# format,压缩的格式,例如:"zip", "tar", "gztar", "bztar", or "xztar".
# root_dir,要压缩的文件夹路径
shutil.make_archive(base_name=r'datafile',format='zip',root_dir=r'files')
# 2. 解压文件
# filename,要解压的压缩包文件
# extract_dir,解压的路径
# format,压缩文件格式
# 解压缩目录不存在时会自动创建
shutil.unpack_archive(filename=r'datafile.zip', extract_dir=r'xxxxxx/xo', format='zip')
7.路径相关
7.1 转义
windows路径使用的是\,linux路径使用的是/。
特别的,在windows系统中如果有这样的一个路径 D:\nxxx\txxx\x1
,程序会报错。因为在路径中存在特殊符 \n
(换行符)和\t
(制表符),Python解释器无法自动区分。
所以,在windows中编写路径时,一般有两种方式:
- 加转义符,例如:
"D:\\nxxx\\txxx\\x1"
- 路径前加r,例如:
r"D:\\nxxx\\txxx\\x1"
7.2 程序当前路径
项目中如果使用了相对路径,那么一定要注意当前所在的位置。
例如:在/Users/wupeiqi/PycharmProjects/CodeRepository/
路径下编写 demo.py
文件
with open("a1.txt", mode='w', encoding='utf-8') as f:
f.write("你好呀")
用以下两种方式去运行:
-
方式1,文件会创建在
/Users/wupeiqi/PycharmProjects/CodeRepository/
目录下。cd /Users/wupeiqi/PycharmProjects/CodeRepository/ python demo.py
-
方式2,文件会创建在
/Users/wupeiqi
目录下。cd /Users/wupeiqi python /Users/wupeiqi/PycharmProjects/CodeRepository/demo.py
【解决程序运行路径问题】
相对路径和绝对路径是一个要命的问题,解决思路:
首先导入os模块,获取当前运行文件所在绝对路径,然后获取当前文件的上级目录绝对路径,使用os.path.join()将目录和文件拼接成最终的绝对路径,这样的好处是,针对不同 平台路径分隔符的不同,它会自动生成对应的路径,而且也没有写死,固定写法如下:
import os
"""
# 1.获取当前运行的py脚本所在路径
abs = os.path.abspath(__file__)
print(abs) # /Users/wupeiqi/PycharmProjects/luffyCourse/day09/20.路径相关.py
path = os.path.dirname(abs)
print(path) # /Users/wupeiqi/PycharmProjects/luffyCourse/day09
"""
base_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(base_dir, 'files', 'info.txt')
print(file_path)
if os.path.exists(file_path):
file_object = open(file_path, mode='r', encoding='utf-8')
data = file_object.read()
file_object.close()
print(data)
else:
print('文件路径不存在')
7.3 文件和路径相关
import shutil
import os
# 1. 获取当前脚本绝对路径
abs_path = os.path.abspath(__file__)
print(abs_path)
# 2. 获取当前文件的上级目录
base_path = os.path.dirname( os.path.dirname(路径) )
print(base_path)
# 3. 路径拼接
p1 = os.path.join(base_path, 'xx')
print(p1)
p2 = os.path.join(base_path, 'xx', 'oo', 'a1.png')
print(p2)
# 4. 判断路径是否存在
exists = os.path.exists(p1)
print(exists)
# 5. 创建文件夹
os.makedirs(路径)
path = os.path.join(base_path, 'xx', 'oo', 'uuuu')
if not os.path.exists(path):
os.makedirs(path)
# 6. 是否是文件夹
file_path = os.path.join(base_path, 'xx', 'oo', 'uuuu.png')
is_dir = os.path.isdir(file_path)
print(is_dir) # False
folder_path = os.path.join(base_path, 'xx', 'oo', 'uuuu')
is_dir = os.path.isdir(folder_path)
print(is_dir) # True
# 7. 删除文件或文件夹
os.remove("文件路径") # 文件和文件夹都可以删除
path = os.path.join(base_path, 'xx')
shutil.rmtree(path)
# 8. 拷贝文件夹
shutil.copytree("/Users/wupeiqi/Desktop/图/csdn/","/Users/wupeiqi/PycharmProjects/CodeRepository/files")
# 将csdn拷贝到files
# 9.拷贝文件
shutil.copy("/Users/wupeiqi/Desktop/图/csdn/WX20201123-112406@2x.png","/Users/wupeiqi/PycharmProjects/CodeRepository/")
shutil.copy("/Users/wupeiqi/Desktop/图/csdn/WX20201123-112406@2x.png","/Users/wupeiqi/PycharmProjects/CodeRepository/x.png")
# 将第一个参数的png拷贝到第二个参数的目录
# 10.文件或文件夹重命名
shutil.move("/Users/wupeiqi/PycharmProjects/CodeRepository/x.png","/Users/wupeiqi/PycharmProjects/CodeRepository/xxxx.png")
shutil.move("/Users/wupeiqi/PycharmProjects/CodeRepository/files","/Users/wupeiqi/PycharmProjects/CodeRepository/images")
# 如果两个参数位于同一个目录,则是重命名,不同目录则是剪切。
【补充知识点】 enumerate 函数(自动生成序号)
data_list = [11, 22, 33, 44, 55]
for i, item in enumerate(data_list, 1):
print(i, item)
# 输出结果:
1 11
2 22
3 33
4 44
5 55
本文来自博客园,作者:#缘起,转载请注明原文链接:https://www.cnblogs.com/yuan-qi/p/15075247.html