一、文件操作
使⽤python来读写⽂件是非常简单的操作. 我们使用open()函数来打开一个文件, 获取到⽂文 件句句柄. 然后通过⽂文件句句柄就可以进行各种各样的操作了了. 根据打开方式的不同能够执行的操 作也会有相应的差异. 打开文件的⽅方式: r, w, a, r+, w+, a+, rb, wb, ab, r+b, w+b, a+b 默认使⽤用的是r(只读)模式
系统对文件的各种操作:
1,将原文件读取到内存。 2,在内存中进行修改,形成新的内容。 3,将新的字符串写入新文件。 4,将原文件删除。 5,将新文件重命名成原文件。
二、只读操作(r,rb, r+,r+b)
f = open("护⼠少妇嫩模.txt",mode="r", encoding="utf-8")
content = f.read() # 在这里要记住,在r模式下encoding 表示解码,在w情况下,是编码
print(content)
f.close()
# 需要注意encoding表⽰示编码集. 根据⽂文件的实际保存编码进⾏行行获取数据, 对于我们⽽而⾔言. 更更 多的是utf-8.
rb. 读取出来的数据是bytes类型, 在rb模式下. 不能选择encoding字符集. 但是涉及到需要解码或则编码是还是需要encoding,
f = open("护⼠少妇嫩模.txt",mode="rb" )
content = f.read()
print(content)
f.close()
#结果: b'xe6xafx85xe5x93xa5, xe5xa4xaaxe7x99xbd, wuse
xe5x91xb5xe5x91xb5
xe6x97xa5xe5xa4xa9'
rb的作⽤用: 在读取非⽂本⽂件的时候. 比如读取MP3. 图像. 视频等信息的时候就需要⽤用到 rb. 因为这种数据是没办法直接显⽰示出来的. 在后⾯面我们⽂文件上传下载的时候还会⽤用到. 还有.
我们看的直播. 实际上都是这种数据.
一般格式: f =open(’文件名(路径)', mode='模式',encoding ='编码方式')特殊格式: with open(’文件名(路径)', mode='模式',encoding ='编码方式') as f:
文件操作流程:打开一个文件(),产生一个文件句柄,对文件句柄进行操作,关闭文件
绝对路径和相对路径:
1. 绝对路径:从磁盘根⽬录开始⼀一直到文件名. 2. 相对路路径:同⼀个文件夹下的⽂件. 相对于当前这个程序所在的⽂件夹⽽而⾔言. 如果在同 一个⽂件夹中. 则相对路径就是这个⽂件名. 如果在上一层⽂件夹. 则要../ (更推荐大家使用相对路径,因为我们在把程序拷贝时,直接把项目拷贝过去就好,不用拷贝C盘等其他盘)
读取的方法(r,r+,rb):
read()将文件的内容全部读取出来, 弊端:相当占内存,如果文件过大,容易导致内存崩溃
f = open("../def/哇擦.txt", mode="r", encoding="utf-8") content = f.read() print(content) #结果: 友谊地久天长, 爱一点, 可惜我是⽔瓶座 一⽣生中最爱 read(n)可以读取n个字符,需要注意的是,如果再次读取,那么将从光标位置读取,如果是rb模式,则是读取n个字节 f = open("../def/哇擦.txt", mode="r", encoding="utf-8") content = f.read(3) content2 = f.read(3) print(content) print(content2) # 结果: 友谊地 久天⻓长 readline() ⼀次读取⼀行数据, 注意: readline()结尾, 注意每次读取出来的数据都会有⼀ 个 所以呢. 需要我们使⽤用strip()⽅方法来去掉 或者空格 readline(n) 当有参数n时,则时读取一行的开头的前n个字: readlines() 将每一⾏形成一个元素, 放到⼀个列表中. 将所有的内容都读取出来. 所以 也是. 容易易出现内存崩溃的问题.不推荐使⽤,形成一个列表
我们来看看:
with open('a.txt', 'r', encoding='utf-8') as f:
s = f.read()
for i in s:
print(i) # 这个运行的结果是,文件中的每一个字符为一行,说明read的结果是生成将每一个字符添加到一个列表中
with open('a.txt', 'r', encoding='utf-8') as f:
a = f.readlines()
for i in a:
print(i) # 而这个结果是分别打印每一行的文字,说明readlines()的结果是将每一行加到一个列表中
with open('a.txt', 'r', encoding='utf-8') as f:
s = f.read()
for i in s:
print(i)
a = f.readlines()
for i in a:
print(i) # 这样的结果是以每一个字符读取换行,因为当下一次循环时,光标已经在末尾了
from collections import Iterable,Iterator
print(isinstance(open('a','w'),Iterable)) #True
print(isinstance(open('a','w'),Iterator)) # True 所以文件句柄是个可迭代的,迭代器
循环读取: f = open("../def/哇擦.txt", mode="r", encoding="utf-8") for line in f: print(line.strip())
三、写模式(w,wb, w+b,w+):
写的时候注意. 如果没有文件. 则会创建文件, 如果⽂件存在. 则将原件中原来的内容删除, 再 写入新内容
f = open("⼩小娃娃", mode="w", encoding="utf-8") f.write("⾦⽑狮王") f.flush() # 刷新. 养成好习惯 f.close() 试试读能读吗? f = open("⼩娃娃", mode="w", encoding="utf-8") f.write("金⽑狮王") f.read() # not readable 模式是w. 不可以执行读操作 f.flush() f.close() wb模式下. 可以不指定打开文件的编码. 但是在写文件的时候必须将字符串转化成utf-8的 bytes数据 f = open("小娃娃", mode="wb") f.write("⾦毛狮王".encode("utf-8")) # 要注意再写的时候一定要写编码的格式 f.flush() f.close() rb, wb, ab, bytes如果处理的是非文本文件, mode里如果有b. encoding就不能给了(以下时复制粘贴图片的底层操作) f = open("c:/pdd.jpg", mode="rb") # 这里不能写encoding e = open("e:/pdd.jpg", mode="wb") for line in f: # 从c盘读取 line你是不知道读取了多少数据的 e.write(line) # 写入到e盘 f.close() e.flush() e.close()
四、追加(a, ab, a+b,a+)
在追加模式下. 我们写入的内容会追加在⽂件的结尾,相当于在这种模式下,打开后光标默认在尾部.
f = open("⼩小娃娃", mode="a", encoding="utf-8") f.write("麻花藤的最爱") f.flush() f.close() # 正常的读取之后, 写在结尾
五、读写模式(r+,r+b)
对于读写模式. 必须是先读. 因为默认光标是在开头的. 准备读取的. 当读完了之后再进行写入. 我们以后使⽤用频率最高的模式就是r+
f = open("⼩小娃娃", mode="r+", encoding="utf-8") content = f.read() f.write("麻花藤的最爱") print(content) f.flush() f.close() # 结果: 正常的读取之后, 写在结尾 错误示范: f = open("⼩小娃娃", mode="r+", encoding="utf-8") f.write("哈哈") content = f.read() print(content) f.flush() f.close() # 结果: 将开头的内容改写成了了"哈哈", 然后读取的内容是后⾯面的内容,因为光标的位置
六、写读(w+ ,w+b)
先将所有的内容清空,然后写入。最后读取,但是读取的内容时空的,不常用
f = open("⼩小娃娃", mode="w+", encoding="utf-8") f.write("哈哈") content = f.read() print(content) f.flush() f.close() # 有人会说. 先读不就好了么? 错. w+ 模式下, ⼀开始读取不到数据. 然后写的时候再将原来的内容清空. 所以, 很少用.
七、追加读(a+)
a+模式下, 不论先读还是后读. 都是读取不到数据的. 除非seek()光标位置
f = open("⼩小娃娃", mode="a+", encoding="utf-8") f.write("⻢马化腾") content = f.read() print(content) f.flush() f.close() # 还有⼀些其他的带b的操作. 就不多赘述了. 就是把字符换成字节. 仅此⽽而已
重要的方法:
1、seek(n) 光标移动到n位置, 注意, 移动的单位是byte. 所以如果是UTF-8的中⽂部要是3的倍数. 通常我们使用seek都是移动到开头或者结尾.
移动到开头: seek(0)
移动到结尾: seek(0,2)
seek的第⼆个参数表示的是从哪个位置进行偏移(初始位置), 默认是0, 表示开头, 1表⽰当前位置, 2表示结尾,seek(偏移度,位置)
2、 tell() 使⽤用tell()可以帮我们获取到当前光标在什么位置
f = open("⼩小娃娃", mode="r+", encoding="utf-8") f.seek(0) # 光标移动到开头 content = f.read() # 读取内容, 此时光标移动到结尾 print(content) f.seek(0) # 再次将光标移动到开头 f.seek(0, 2) # 将光标移动到结尾 content2 = f.read() # 读取内容. 什什么都没有 print(content2) f.seek(0) # 移动到开头 f.write("张国荣") # 写⼊入信息. 此时光标在9 中⽂文3 * 3个 = 9 print(f.tell()) # 光标位置9 f.flush() f.close()
3. truncate() 截断⽂件 使用的是光标f = open("⼩娃娃", mode="w", encoding="utf-8")
f.write("哈哈") # 写入两个字符
f.seek(3) # 光标移动到3, 也就是两个字中间
f.truncate() # 删掉光标后⾯面的所有内容
f.close()
f = open("⼩小娃娃", mode="r+", encoding="utf-8")
content = f.read(3) # 读取12个字符
f.seek(4)
print(f.tell())
f.truncate() # 后面的所有内容全部都删掉
# print(content)
f.flush()
f.close()
注意事项:
深坑请注意: 在r+模式下. 如果读取了内容. 不论读取内容多少. 光标显示的是多少. 再写入 或者操作文件的时候都是在结尾进行的操作. 所以如果想做截断操作. 记住了. 要先挪动光标. 挪动到你想要截断的位置. 然后再进行截断 关于truncate(n), 如果给出了n. 则从开头进行截断, 如果不给n, 则从当前位置截断. 后⾯的内容将会被删除
八.修改文件以及另一种打开方式:
⽂文件修改: 只能将⽂文件中的内容读取到内存中, 将信息修改完毕, 然后将源⽂文件删除, 将新 ⽂文件的名字改成老⽂文件的名字.
# 第一种修改方式: import os with open("⼩小娃娃", mode="r", encoding="utf-8") as f1, open("⼩小娃娃_new", mode="w", encoding="UTF-8") as f2: content = f1.read() new_content = content.replace("冰糖葫芦", "⼤大⽩白梨梨") f2.write(new_content) os.remove("⼩小娃娃") # 删除源⽂文件 os.rename("⼩小娃娃_new", "⼩小娃娃") # 重命名新⽂文件 #但是以上一种方法有弊端,因为其时一次性讲整个文件读入内存,若文件过大会崩溃 #推荐以下方法: with open("⼩小娃娃", mode="r", encoding="utf-8") as f1, open("⼩小娃娃_new", mode="w", encoding="UTF-8") as f2: for line in f1: new_line = line.replace("⼤大⽩白梨梨", "冰糖葫芦") f2.write(new_line) os.remove("⼩小娃娃") # 删除源⽂文件 os.rename("⼩小娃娃_new", "⼩小娃娃") # 重命名新⽂文件
练习题:
1.文件a1.txt内容(升级题) 序号 部门 人数 平均年龄 备注 1 python 30 26 单身狗 2 Linux 26 30 没对象 3 运营部 20 24 女生多 ....... 通过代码,将其构建成这种数据类型: [{'序号':'1','部门':Python,'人数':30,'平均年龄':26,'备注':'单身狗'}, ......] l1 = [] with open('a2.txt', encoding='utf-8') as f1: # 读取第一行,去除空格,使用空格切割成列表 list_name = f1.readline().strip().split() # 从第二行开始读取,因为读取一行之后,光标自动移动到下面一行。 # 所以for循环不会读取到第一行 for i in f1: dic = {} # 去除空格,以空格切割成列表 i = i.strip().split() # 遍历i for j in range(len(i)): # 添加字典, list_name[j]表示key, i[j]表示value,比如'序号': '1' dic[list_name[j]] = i[j] # 添加到列表中 l1.append(dic) print(l1) 2.文件a1.txt内容(升级题) name:apple price:10 amount:3 year:2012 name:tesla price:100000 amount:1 year:2013 ....... 通过代码,将其构建成这种数据类型: [{'name':'apple','price':10,'amount':3}, {'name':'tesla','price':1000000,'amount':1}......] 并计算出总价钱。 li = [] with open('a11.txt', 'r', encoding='utf-8') as f: for i in f: a = i.strip().split(' ') dic = {} for i in a: b = i.split(':') if b[1].isdigit(): dic[b[0]] = int(b[1]) else: dic[b[0]] = b[1] li.append(dic) print(li) sum = 0 for i in range(len(li)): sum +=li[i]['price'] * li[i]['amount'] # print(li) print('总计消费%d'%sum)
1、缓存机制和小数据池: 不同代码块的缓存机制: Python在执行同一个代码块的初始化对象的命令时,会检查是否其值是否已经存在,如果存在,会将其重用。换句话说:执行同一个代码块时,遇到初始化对象的命令时,他会将初始化的这个变量与值存储在一个字典中,在遇到新的变量时,会先在字典中查询记录,如果有同样的记录那么它会重复使用这个字典中的之前的这个值。所以在你给出的例子中,文件执行时(同一个代码块)会把i1、i2两个变量指向同一个对象,满足缓存机制则他们在内存中只存在一个,即:id相同。 小数据池: Python自动将-5~256的整数进行了缓存,当你将这些整数赋值给变量时,并不会重新创建对象,而是使用已经创建好的缓存对象。python会将一定规则的字符串在字符串驻留池中,创建一份,当你将这些字符串赋值给变量时,并不会重新创建对象, 而是使用在字符串驻留池中创建好的对象。其实,无论是缓存还是字符串驻留池,都是python做的一个优化,就是将~5-256的整数,和一定规则的字符串,放在一个‘池’(容器,或者字典)中,无论程序中那些变量指向这些范围内的整数或者字符串,那么他直接在这个‘池’中引用,言外之意,就是内存中之创建一个。 2、编程题: 有文件t1.txt里面的内容为:(5分) id,name,age,phone,job 1,alex,22,13651054608,IT 2,wusir,23,13304320533,Tearcher 3,taibai,18,1333235322,IT 利用文件操作,将其构造成如下数据类型。 [{'id':'1','name':'alex','age':'22','phone':'13651054608','job':'IT'},......] l=[] with open('t1.txt','r',encoding = 'utf-8') as f: li = f.readline().strip().split(',') for i in f: lst = i.strip().split(',') for i in li: dic= dict([(li[0],lst[0]),(li[1],lst[1]),(li[2],lst[2]),(li[3],lst[3]),(li[4],lst[4])]) l.append(dic) print(l) 车牌区域划分,给出一下车牌和地点信息对照,请根据车牌信息,分析出各省的车牌持有数量。 cars = ['鲁A32444', '鲁B12333', '京B8989M', '黑C49678', '黑C46555', '沪B25041', '黑C34567'] locations = {'沪': '上海', '京': '北京', '黑': '黑龙江', '鲁': '山东', '鄂': '湖北', '湘': '湖南'}(6分) 最终形成这样的数据:{'山东': 2, '黑龙江': 3, '北京': 1, '上海': 1} 法一: result = {} for car in cars: result[locations[car[0]]] = result.get(locations[car[0]], 0) + 1 print(result) 法二: for i in cars: if locations[i[0]] not in li: li[locations[i[0]]] = 1 else:li[locations[i[0]]] += 1 print(li) 按要求完成下列转化。(8分) list3 = [ {"name": "alex", "hobby": "抽烟"}, {"name": "alex", "hobby": "喝酒"}, {"name": "alex", "hobby": "烫头"}, {"name": "alex", "hobby": "Massage"}, {"name": "wusir", "hobby": "喊麦"}, {"name": "wusir", "hobby": "街舞"}, ] list4 = [ {"name": "alex", "hobby_list": ["抽烟", "喝酒", "烫头", "Massage"]}, {"name": "wusir", "hobby_list": ["喊麦", "街舞"]}, ] 将list3 这种数据类型转化成list4类型,你写的代码必须支持可拓展, 比如list3 数据在加一个这样的字典{"name": "wusir", "hobby": "溜达"}, 你的list4{"name": "wusir", "hobby_list": ["喊麦", "街舞", "溜达"], 或者list3增加一个字典{"name": "太白", "hobby": "开车"}, 你的list4{"name": "太白", "hobby_list": ["开车"],无论按照要求加多少数据,你的代码都可以转化.如果不支持拓展,则4分,支持拓展则8分. list3 = [ {"name": "alex", "hobby": "抽烟"}, {"name": "alex", "hobby": "喝酒"}, {"name": "alex", "hobby": "烫头"}, {"name": "alex", "hobby": "Massage"}, {"name": "wusir", "hobby": "喊麦"}, {"name": "wusir", "hobby": "街舞"}, ] dic ={} for i in list3: if i['name'] not in dic: dic[i['name']] = {'name':i['name'],'hobby_list':[i['hobby']]} else: dic[i['name']]['hobby_list'].append(i['hobby']) list4 = list(dic.values()) print(list4)