0%

六、学习py:文件操作的相关操作

六、学习py:文件操作的相关操作

1.文件操作

在正式学习文件操作之前,大火先来看一下数据类型和编码的一些知识。

1.字符串类型(str),在程序中用于表示文字信息,本质上是unicode编码中的二进制。

2.字节类型(bytes)可表示文字信息,本质上是utf-8\gbk编码的二进制(对unicode进行了压缩方便文件存储和网络传输。)

图片也可以表示。

1.1读文件

1.1.1读文本文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#1.打开文件
# -路径
# 有相对路径和绝对路径
# -模式
# rb,表示读取文件的原始的二进制(r,read;b,二进制,binary)

#打开文件
file_object = open('info.txt',mode='rb')
#读取文件
data = file_object.read()
#关闭文件
file_object.close
print(data)#这里读取的是字节类型
#这里面的file_object是一个对象,代表了'info.txt'那个文件
text = data.decode("utf-8")
print(text)#这里读出来就是文本了
1
2
3
#在py中读文件的时候,内置了一个模块。
file_object = open('info.txt',mode='rt',encoding = 'utf-8')
#这样读出来就直接是文本。

1.1.2读图片文件

1
2
3
file_object = open('dhk.png',mode='rb')
data = file_object.read()
file_object.close#这里读出来也是二进制。

注意:

1.在输入路径的时候,我建议还是使用绝对路径,相对路径很有可能会变化。

2.在写路径的时候,在路径的引号前面加上r,例如:防止系统将\new的\n认为是换行符。

3.如果文件不存在,程序会报错。

1.1.3扩展:判断路径是否存在

1
2
3
import os
exists = os.path.exists("info.txt")
print(exists)#这里返回的是一个布尔值

1.2写文件

1.2.1写文本文件

1
2
3
file_object = open("ex.txt",mode='wb')#这个wb是指,write,二进制
file_object.write("dhk".encode('utf-8'))
file_object.close()
1
2
3
4
file_object = open("ex.txt",mode='wt',encoding='utf-8')
file_object.write("dhk")
file_object.close()
#默认的encoding就是utf-8,当然你也可以自己去指定,所以我更推荐这种方法,不用每次写入都进行加密。

1.2.2写图片文件

1
2
3
4
5
6
7
f1 = open('ex.png',mode = rb)
content = f1
f1.close()

f2 = open('ex2.png',mode = wb)
f2.write(content)
f2.close()

1.2.3案例,实现用文本储存账户

1
2
3
4
5
6
7
8
username = input("请输入您的用户名字:")
password = input("请输入您的密码: ")

list = {f"{username}":f"{password}"}
content = open("info.txt",mode='wt',encoding='utf-8')
content.write(f"{list}")
content.close()
#使用w写入文件的时候,会先清空文件,再在文件中写入内容。并不会直接在原来的文档里面加入内容,所以要实现多用户注册,就不能让他多次open文件。

1.2.4案例,实现多用户的注册

1
2
3
4
5
6
7
8
9
10
11
12
content = open("info.txt", mode='wt', encoding='utf-8')
while True:
username = input("请输入您的用户名字(输入q退出):")
if username.upper() == "Q":
break
password = input("请输入您的密码: ")
list = {f"{username}":f"{password}"}
n = "\n"
content.write(f"{list}"+f"{n}")
content.close()

#将文件的开关都放在while循环的外面,这样就不会多次打开文件导致文件的内容清零,相对的关闭文件也要放在循环外面,防止多次关闭文件导致程序报错。

1.2.5案例,实现在网络上获取内容并且写入文件

在这里会使用一个第三方的模块(可以伪造浏览器向网站发送请求)

使用终端安装

pip install requests

模块的使用方法(这里只是在这里说一下常用的一个方法,关于模块后面会介绍)

1
2
3
4
5
6
import requests
r=requests.get("http://www.baidu.com")
print(r)
r.status_code
print(r.content)
#后面会讲到,然后这里再在后面加入写入文件的功能就好了.

1.3文件打开模式

1.3.1下面将介绍各种模式

r 读取

w 重新编写这个文件

x 创建一个新的文件,并进行编辑

a 打开一个文件,在它的末尾加入内容

b 二进制模式

t文本模式

image-20211230172323356

扩展:

1
text.seek(0)#将光标置于第一个位置,也就是起始位置。

python文件操作打开模式 r,w,a,r+,w+,a+ 区别辨析 - 少年与python - 博客园 (cnblogs.com)

1.4常见功能

在上述中我们只使用了读写功能,其实还有很多的辅助功能。

1.4.1read读所有

1
2
f = open('info.txt',mode = 'r',encoding='utf-8')
data = f.read()

1.4.2read()读指定的内容

在文件上传的时候大多也会使用这种方式,一点字节一点字节的发送。

1
2
f = open('info.txt',mode = 'r',encoding='utf-8')
data = f.read(2)#读取2个字符
1
2
3
4
5
6
7
8
f = open('info.txt',mode = 'r',encoding='utf-8')
a = f.read(1)#读取第1个字符
b = f.read(1)
c = f.read(1)
d = f.read(1)
e = f.read(1)
print(a,b,c,d,e)
#一个一个字符的读取,由于光标在每一次读取后都会往后推进一个,所以每一次读取的内容是不一样的。

1.4.3readline()读一行

1
2
3
f = open('info.txt',mode = 'r',encoding='utf-8')
data = f.readline(2)#读取第二行的内容
#一般用于处理庞大的文件,如果直接用read会导致内存崩溃,but readline每次只读取一行内容,极大的减少了内存的负担。

1.4.4readlines()读取所有行,返回一个列表,其中每一个元素是每一行的内容。

1.4.5for循环

1
2
3
4
5
6
7
8
9
10
f = open('info.txt',mode = 'r',encoding='utf-8')
for line in f:
print(line.strip())#删除每一行的换行符,因为每一次循环print的时候也会有一个换行符。
#输出
'''
小丑
动感
joker
你好
'''

1.4.6write,写

1
2
3
f = open('info.txt',mode = 'a',encoding='utf-8')
f.write("dhk")
f.close

1.4.7flush,刷到硬盘

1
2
3
4
5
6
f = open('info.txt',mode = 'a',encoding='utf-8')
while True:
f.write("dhk")
#不是写到了硬盘,而是写在缓冲区,系统会将缓冲区的内容刷到硬盘。
f.flush()#直接刷到硬盘
f.close

1.4.8移动光标位置(字节)

1
2
3
f = open('info.txt',mode (= 'r+',encoding='utf-8')
f.seek(1)
#将光标移动到指定位置。

注意:在a模式下,write只会写到文件的最后,不会写到指定的光标位置。

1.4.9获取光标位置

1
2
3
f = open('info.txt',mode (= 'r+',encoding='utf-8')
p1 = f.tell()
print(p1)

1.5上下文管理

之前对文件进行操作的时候,每次都要打开和关闭文件,比较繁琐且容易忘记关闭文件。

以后再进行文件操作时,推荐大家使用with上下文管理,他可以实现自动关闭文件。

1
2
3
4
with open('info.txt',mode (= 'r+',encoding='utf-8') as f
data = f.read()
print(data)
#在缩进结束的时候,他就会自动的帮你关闭文件。

在python2.7后,with又支持同时对多个文件的上下文进行管理,即:

1
2
with open('info.txt',mode (= 'r+',encoding='utf-8') as f,open('info2.txt',mode (= 'r+',encoding='utf-8') as f2
pass

1.6功能扩展:从指定的网址爬取视频

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
headers = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
}#指定文件头
url ="https://video.pearvideo.com/mp4/adshort/20200709/cont-1684816-15252785_adpkg-ad_hd.mp4"
response = requests.get(url,headers=headers,stream=True)
#介绍一下这个stream模块
#当stream=True时,如果是下载大的文件时,用True,可以先对请求的类型进行判断,如果是大文件,可以中止请求,而不用浪费大流量开销。如果不加stream=True,那么硬盘很可能就不停被写入,文件会变得无比巨大,最后磁盘空间不够死机。
#当stream=False时,如果是请求的大文件,其会进入内存并进行下载,消费大量的内存和流量。
print(response.status_code)
print(response.headers['content-length'])
content_size = int(response.headers['content-length'])
# print(response.text)
# print(response.content)
n = 1
with open("v.mp4","wb") as f:
for i in response.iter_content(chunk_size=1024):#这个进度条我是偷别人的嘿嘿。
rate=n*1024/content_size
print("下载进度:{0:%}".format(rate))
f.write(i)
n+=1
print("下载完成")

第二个扩展,将一个文件中的其中一个字符修改成另外一个字符

小文件:文件读到内存,再通过replace

大小文件都可以使用:同时打开两个文件,读+写

这里演示一下第二种思路。

1
2
3
4
5
6
7
with open('info.txt',mode='r') as c1 , open('new.txt',mode='a') as c2:
for line in c1:
new_line = line.replace('joker','dhk')
c2.write(f'{new_line}')
#重命名文件之后就可以覆盖掉原文件
#imort shutil
#shutil.move('new.txt','info.txt')

2.csv格式文件

逗号分隔值(有时候也被称为字符分隔值),其文件以纯文本形式 储存表格数据(数字和文本)。

对于这种格式的数据,我们需要利用open函数来读取文件并根据逗号分隔的特点进行处理。

2.1扩充函数

在当前根目录下创建一个文件夹。

1
os.makedirs("name")

3.ini格式

ini文件是initialization File的缩写,平时用于储存软件的配置文件。例如:Mysql数据库的配置文件。

ini文件示例说明 - 落冰 - 博客园 (cnblogs.com)

这种格式是可以直接使用open出来,考虑到自己处理比较麻烦,所以python为我们提供了更为方便的方式。

3.1读取所有节点

1
2
3
4
import configparser
config = configparser.ConfigParser()
config.read('mysql.ini',encoding='utf-8')#可以是相对路径也可以是绝对路径
result = config.sections()#获取所有的节点,返回一个列表

3.2获取节点下面的键值

1
2
3
4
import configparser
config = configparser.ConfigParser()
config.read('mysql.ini',encoding='utf-8')#可以是相对路径也可以是绝对路径
result = config.items('节点')#获取指定节点下面的键值,返回一个嵌套着元组的列表。

3.3获取某一个节点下的键对应的值

1
2
3
4
import configparser
config = configparser.ConfigParser()
config.read('mysql.ini',encoding='utf-8')#可以是相对路径也可以是绝对路径
result = config.get('节点','键')#获取某一个节点下的键对应的值

3.4其他功能

3.4.1判断文件是否有这个节点

1
2
3
4
import configparser
config = configparser.ConfigParser()
config.read('mysql.ini',encoding='utf-8')#可以是相对路径也可以是绝对路径
result = config.has_section('节点')#判断是否有这个节点,返回布尔值。

3.4.2添加一个节点

1
2
3
4
5
6
import configparser
config = configparser.ConfigParser()
config.read('mysql.ini',encoding='utf-8')#可以是相对路径也可以是绝对路径)
config.add_section('节点')#只是写到内存中,但是没有到硬盘中
config.set('节点','键','值',)
config.write(open('文件名',mode='w',encoding='utf-8'))#这样才会写到文件里面

3.4.3删除

删除节点

1
2
3
4
5
import configparser
config = configparser.ConfigParser()
config.read('mysql.ini',encoding='utf-8')#可以是相对路径也可以是绝对路径)
config.remove_section('节点')
config.write(open('文件名',mode='w',encoding='utf-8'))

删除键

1
config.remove_option('节点','键')

4.XML格式文件

可扩展标记语言,是一种简单的数据存储语言,XML被设计用来传输和存储数据。

1.存储,可用来存放配置文件,例如:java的配置文件。

2.传输,网络传输是以这种格式存在,例如早期ajax传输的数据集,soap协议等。

(27条消息) XML文件详解以及解析_浮华蒲公英的博客-CSDN博客_xml文件

4.1以country.xml为例,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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>

4.2解析

1)调用parse()方法,返回解析树

1
python3.3之后ElementTree模块会自动寻找可用的C库来加快速度
1
2
3
4
5
6
7
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET

tree = ET.parse("country.xml") # <class 'xml.etree.ElementTree.ElementTree'>
root = tree.getroot() # 获取根节点 <Element 'data' at 0x02BF6A80>
2)调用from_string(),返回解析树的根元素
1
2
3
import xml.etree.ElementTree as ET
data = open("country.xml").read()
root = ET.fromstring(data) # <Element 'data' at 0x036168A0>

3)调用ElementTree类ElementTree(self, element=None, file=None) # 这里的element作为根节点

1
2
3
import xml.etree.ElementTree as ET
tree = ET.ElementTree(file="country.xml") # <xml.etree.ElementTree.ElementTree object at 0x03031390>
root = tree.getroot() # <Element 'data' at 0x030EA600>

4.3遍历

1)简单遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import xml.etree.ElementTree as ET

tree = ET.parse("country.xml")
root = tree.getroot()
print(root.tag, ":", root.attrib) # 打印根元素的tag和属性

# 遍历xml文档的第二层
for child in root:
# 第二层节点的标签名称和属性
print(child.tag,":", child.attrib)
# 遍历xml文档的第三层
for children in child:
# 第三层节点的标签名称和属性
print(children.tag, ":", children.attrib)

可以通过下标的方式直接访问节点

1
2
# 访问根节点下第一个country的第二个节点year,获取对应的文本
year = root[0][1].text # 2008

2)ElementTree提供的方法

  • find(match) # 查找第一个匹配的子元素, match可以时tag或是xpaht路径
  • findall(*match*) # 返回所有匹配的子元素列表
  • findtext(match, default=None) #
  • iter(tag=None) # 以当前元素为根节点 创建树迭代器,如果tag不为None,则以tag进行过滤
  • iterfind(*match*) #
1
2
3
# 过滤出所有neighbor标签
for neighbor in root.iter("neighbor"):
print(neighbor.tag, ":", neighbor.attrib)
1
2
3
4
5
6
7
# 遍历所有的counry标签
for country in root.findall("country"):
# 查找country标签下的第一个rank标签
rank = country.find("rank").text
# 获取country标签的name属性
name = country.get("name")
print(name, rank)

4.4修改xml结构

  1. 属性相关
1
2
3
4
5
6
7
8
9
10
11
# 将所有的rank值加1,并添加属性updated为yes
for rank in root.iter("rank"):
new_rank = int(rank.text) + 1
rank.text = str(new_rank) # 必须将int转为str
rank.set("updated", "yes") # 添加属性

# 再终端显示整个xml
ET.dump(root)
# 注意 修改的内容存在内存中 尚未保存到文件中
# 保存修改后的内容
tree.write("output.xml")
1
2
3
4
5
6
7
8
9
10
11
import xml.etree.ElementTree as ET

tree = ET.parse("output.xml")
root = tree.getroot()

for rank in root.iter("rank"):
# attrib为属性字典
# 删除对应的属性updated
del rank.attrib['updated']

ET.dump(root)
  1. 节点/元素 相关
1
2
3
4
5
6
7
8
9
10
11
12
13
import xml.etree.ElementTree as ET

tree = ET.parse("country.xml")
root = tree.getroot()

# 删除rank大于50的国家
for country in root.iter("country"):
rank = int(country.find("rank").text)
if rank > 50:
# remove()方法 删除子元素
root.remove(country)

ET.dump(root)

添加子元素

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import xml.etree.ElementTree as ET

tree = ET.parse("country.xml")
root = tree.getroot()

country = root[0]
last_ele = country[len(list(country))-1]
last_ele.tail = '\n\t\t'
# 创建新的元素, tag为test_append
elem1 = ET.Element("test_append")
elem1.text = "elem 1"
# elem.tail = '\n\t'
country.append(elem1)

# SubElement() 其实内部调用的时append()
elem2 = ET.SubElement(country, "test_subelement")
elem2.text = "elem 2"

# extend()
elem3 = ET.Element("test_extend")
elem3.text = "elem 3"
elem4 = ET.Element("test_extend")
elem4.text = "elem 4"
country.extend([elem3, elem4])

# insert()
elem5 = ET.Element("test_insert")
elem5.text = "elem 5"
country.insert(5, elem5)

ET.dump(country)

4.5创建xml文档

想创建root Element,然后创建SubElement,最后将root element传入ElementTree(element),创建tree,调用tree.write()方法写入文件

对于创建元素的3个方法: 使用ET.Element、Element对象的makeelement()方法以及ET.SubElement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import xml.etree.ElementTree as ET


def subElement(root, tag, text):
ele = ET.SubElement(root, tag)
ele.text = text
ele.tail = '\n'


root = ET.Element("note")

to = root.makeelement("to", {})
to.text = "peter"
to.tail = '\n'
root.append(to)

subElement(root, "from", "marry")
subElement(root, "heading", "Reminder")
subElement(root, "body", "Don't forget the meeting!")

tree = ET.ElementTree(root)
tree.write("note.xml", encoding="utf-8", xml_declaration=True)

由于原生保存的XML时默认无缩进,如果想要设置缩进的话, 需要修改保存方式

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import xml.etree.ElementTree as ET
from xml.dom import minidom


def subElement(root, tag, text):
ele = ET.SubElement(root, tag)
ele.text = text


def saveXML(root, filename, indent="\t", newl="\n", encoding="utf-8"):
rawText = ET.tostring(root)
dom = minidom.parseString(rawText)
with open(filename, 'w') as f:
dom.writexml(f, "", indent, newl, encoding)


root = ET.Element("note")

to = root.makeelement("to", {})
to.text = "peter"
root.append(to)

subElement(root, "from", "marry")
subElement(root, "heading", "Reminder")
subElement(root, "body", "Don't forget the meeting!")

# 保存xml文件
saveXML(root, "note.xml")

5.Excel格式文件

python中没有提供处理Excel文件的功能,想要在python中操作Excel需要安装第三方的模块

1
pip install openpyxl

5.1读Excel

5.1.1读sheet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from openpyxl import load_wrokbook
wb = load_wrokbook('#文件路径')
#sheet相关操作
#1获取所有sheet
print(wb.sheetnames)#返回一个列表,获取所有sheet
#2.选中一个sheet,基于sheet名称
sheet = wb["#sheet名称"]
print(sheet.cell(1,1)#读取第一行第一列的单元格)
#3.选中一个sheet,基于索引位置
sheet = wb.worksheets[0]#取索引位置为0的sheet
#4.循环所有的sheet
for name in wb.sheetnames:
sheet = wb[name]

for sheet in wb,worksheets:
cell = sheet.cell(1,1)

5.1.2读取单元格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from openpyxl import load_wrokbook
wb = load_wrokbook('#文件路径')
print(wb.sheetnames)
sheet = wb["#sheet名称"]
#获取第n行第n列单元格
cell = sheet.cell(1,1)#这里要注意单元格是从1开始的,不是从0开始的。
cell.value#即为单元格中的文本内容
cell.style#样式
cell.font#字体
cell.alignment#排列情况,居中等

#获取第n行所有的单元格
for cell in sheet[1]:
print(cell.value)

#获取所有行的数据
for row in sheet.rows:#每一行是一个元组,所以第一个索引是0开始。
print(row)

#获取每一列的数据
for columns in sheet.clumnss:
print (clumns.value)

5.2写Excel

在Excel中想要写文件,大致要分为在:

5.2.1在原Excel文件基础上写内容。

1
2
3
4
5
6
7
8
9
10
from openpyxl import workbook
wb = load_workbook('files/p1.xlsx')
sheet = wb.worksheetts(0)

#找到单元格,并修改单元格内容
cell = sheet.cell(1,1)
cell.value = '新的开始'

#将Excel文件保存的到p2.xlsx文件中
wb.save('files/p2.xlsx')

5.2.2新创建Excel文件写内容

1
2
3
4
5
6
7
8
9
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.sava("file/p2.xlsx")

6.压缩文件

基于python内置的shutil模块可以实现对压缩文件的操作。

1
2
3
4
5
6
import shutil
#1.压缩文件
#base_name 压缩后的压缩包文件
#format 压缩的格式,例如“zip”,“tar”
#root_dir 要压缩的文件路径
shutil.make_archive(base_name=r'D:\xxx\xxx\xx',format='zip',root_dir=r'D:\xxx\xxx\xx')
1
2
3
4
5
#2.解压文件
#filename,要解压的压缩包文件
#extract_dir,解压的路径
#format,压缩文件格式
shutil.unpack_archive(filename=r"D:\xxx\xxx\xx',extract_dir=r'D:\xxx\xxx\xx',format='zip')#这里路径前面加上r的目的是为了防止斜杠和字母形成特殊符号。

7.路径相关

7.1查看目录

1
2
3
4
5
6
import os
abs = os.path.abspath(__file__)#里面的__file__代指的就是你当前的那个脚本
#获取当前运行的脚本的绝对路径路径
path = os.path.dirname(abs)#获取的是当前文件的上一级目录的路径
file = os.path.dirname(os.path.abspath(__file__))#平时写代码的时候就可以这样把他们拼接起来来获取上一级目录的绝对路径,然后再拼接上文件的名字就可以了,也可能是在你的脚本目录下面的目录哦(๑╹◡╹)ノ”
file_path = file + '文件名字'

7.2文件夹

7.2.1创建文件夹

1
2
3
4
5
#os.makedirs(路径)
#案例
path = os.path.join(base_path,'xx','oo','uuuu')
if not os.path.exists(path):#如果路径不存在
os.makedirs(path)#创建一个文件夹

7.2.2是否为文件夹

1
os.path.isdir(路径)#判断是否为文件夹,返回一个布尔值

7.2.3删除文件或文件夹

1
shutil.rmtrree(path)

7.2.4拷贝文件夹

1
shutil.copytree("要拷贝的文件夹","拷贝到这里")

7.2.5拷贝文件

1
shutil.copy("要拷贝的文件","拷贝到这里")

7.2.6文件或文件夹重命名

1
shutil.move("要移动的文件","移动到这里")#目录要是一样的话就是重命名了(*´▽`)ノノ