> 文章列表 > pt09文件处理

pt09文件处理

pt09文件处理

文件处理

  • 文件分类

    • 文本文件:打开后会自动解码为字符,如txt文件,word文件,py程序文件。
    • 二进制文件:内部编码为二进制码,无法通过文字编码解析,如压缩包,音频,视频,图片等。

    字符串与字节串相互转化方法

    str.encode()     变量或者包含非英文字符的字符串转换为字节串
    bytes.decode()   字节串转换为字符串"""
    字节串学习所有字符串都能转换为字节串   是
    所有字节串都能转换为字符串   否
    """#  ascii 字符 字节串
    byte1 = b"Hello world"    #纯英文字符字符串常量可以在前面加b转换为字节串
    print(type(byte1))    #<class 'bytes'>
    print(byte1) 	#b'Hello world'# utf-8 字符----->字节串   encode() str->bytes
    byte2 = "你好".encode()
    print(byte2)  	#b'\\xe4\\xbd\\xa0\\xe5\\xa5\\xbd'# 字节串--->字符串   decode()
    print(byte2.decode())  	#你好
    print(b'\\xe4\\xbd\\xd6\\xe3\\xa4\\xbd'.decode())  	#报错
    

文件操作

打开文件 open

file_object = open(file_name, access_mode='r', buffering=-1,encoding=None)功能:打开一个文件,返回一个文件对象。
参数:file_name  文件名;access_mode  打开文件的方式,如果不写默认为‘r’ buffering  1表示有行缓冲,默认则表示使用系统默认提供的缓冲机制。encoding='UTF-8'  设置打开文件的编码方式,一般Linux下不需要
返回值:成功返回文件操作对象。
打开模式 效果
r 以读方式打开,文件必须存在
w 以写方式打开,文件不存在则创建,存在清空原有内容
a 以追加模式打开,文件不存在则创建,存在则继续进行写操作
r+ 以读写模式打开 文件必须存在
w+ 以读写模式打开文件,不存在则创建,存在清空原有内容
a+ 追加并可读模式,文件不存在则创建,存在则继续进行写操作
rb 以二进制读模式打开 同r
wb 以二进制写模式打开 同w
ab 以二进制追加模式打开 同a
rb+ 以二进制读写模式打开 同r+
wb+ 以二进制读写模式打开 同w+
ab+ 以二进制读写模式打开 同a+

注意 :

​ 以二进制方式打开文件,读取内容为字节串,写入也需要写入字节串
​ 二进制文件则不能以文本方式打开,文件都可以使用二进制方式打开。

文件打开代码示例:open_file.py# 读方式打开文件
# file = open("../day02/2.txt","r")# 写方法打开
# file = open("file.txt","w") # 清除原来内容
file = open("file.txt","a") # 不会清除原来内容# 关闭
file.close()

读取文件

read([size]) 直接读取文件中字符

二进制文件不会换行,使用read较多

size参数:最多读取给定数目个字符,如果没有或为负(默认-1),文件将被读取直至末尾,
返回值: 返回读取到的内容
注意:1.文件过大时候不建议直接读取到文件结尾,占用内存较多,效率较低2.读到文件结尾如果继续进行读操作会返回空字符串(空字符串不是换行\\n)file = open("file.txt",'r',encoding='UTF-8') #windows系统要带encoding='UTF-8'
#open后每次读取都是从上一次位置向后读data = file.read(1)
print(data)
data = file.read(1)
print(data)
readline([size]) 用来读取文件中一行
如果没有给定size参数(默认值为-1)或者size值为负,表示读取一行,给定size表示最多读取指定的字符(字节)。
返回值: 返回读取到的内容
readlines([sizeint]) 用来读取文件中一行
readlines([sizeint])
读取文件中的每一行作为列表中的一项
默认文件将被读取直至末尾,给定size表示读取到size字符所在行为止。
返回值: 返回读取到的内容列表
for 循环迭代文件的每一行
# 文件对象本身也是一个可迭代对象,在for循环中可以迭代文件的每一行。for line in f:print(line)
"""
文件读操作open后每次读取都是从上一次位置向后读读到文件结尾继续读会返回空字串读取大文件时使用循环读取比一次性read效率更高
"""# 读方式打开文件
file = open("file.txt",'r',encoding='UTF-8') #windows系统要带encoding='UTF-8'# 读取内容
data = file.read()
print(data)
#file = open("file.txt", 'rb')  # 二进制打开
#data = file.read()
#print(data.decode())   #二进制读取# 循环读取
# while True:
#     data = file.read(1)
#     if not data:
#         break
#     print(data)
#     print(data,end="")# 按照行读取
# line = file.readline(2)
# print(line)
# line = file.readline()
# print(line)# 读取所有行形成列表
# lines = file.readlines(12)
# print(lines)# 迭代获取
# for line in file:
#     print(line) # 每天一行内容# 关闭
file.close()

练习

"""
使用dict.txt文件完成:
编写一个函数,参数传入一个单词,返回值返回
这个单词及解释提示: 每个单词占一行,单词与解释之间一定有空格,单词按照顺序排列逻辑思路: 逐行比对 首个但是否为参数
"""def query_word(word):file = open("dict.txt")  # 默认 r# 每次取一行abandonmentfor line in file:head = line.split(' ')[0]  # 提取首个单词if head > word:break  # 如果遍历到的单词已经比word大就不用再找了elif word == head:return line  # 返回这一行print(query_word("abc"))# 第二种判断
# n = len(word)
# line[:n] == word and line[n]==' '

写入文件

write(data)
功能: 把文本数据或二进制数据块的字符串写入到文件中去
参数:要写入的内容。如果需要换行要自己在写入内容中添加\\n
返回值:写入的字符个数
writelines(str_list)
功能:接受一个字符串列表作为参数,将它们写入文件。
参数: 要写入的内容列表,没有返回值

文件写操作代码示例:

# 写打开
file = open("file.txt","w")    #  windows系统记得加   encoding='utf-8'
# file = open("file.txt","a") # 追加# 写操作
#file = open("file.txt","wb")
# n = file.write("hello\\n".encode())   #写的时候类型要一致,不影响文本显示n = file.write("hello\\n")    #格式控制需要自己定义,不然会接连着写
file.write("hello\\n")
print("写入字符数:",n)   #写入字符数: 6# # 列表中每一项分别写入
data = ["小明\\n", "小刚\\n", "小妹"]
file.writelines(data)# 将列表每一项写入到文件
data = ["接着奏乐\\n","接着舞\\n"
]file.writelines(data)file.close()

练习

编写一个函数,参数传入一个指定的文件,将这个文件
复制到程序运行目录下,注意不确定文件类型
思路 : 边读边写def copy(filename):"""将指定文件复制到这个文件夹下:param filename: 指定的要复制的文件"""new_file = filename.split('/')[-1] # 取出文件名fr = open(filename,"rb") # 原文件fw = open(new_file,"wb") # 新文件# 边读边写while True:data = fr.read(1024)if not data:breakfw.write(data)fr.close()fw.close()copy("/home/b.jpg")   
close关闭文件

打开一个文件后我们就可以通过文件对象对文件进行操作了,当操作结束后可以关闭文件操作

file_object.close()
  1. 可以销毁对象节省资源,(当然如果不关闭程序结束后对象也会被销毁)。
  2. 防止后面对这个对象的误操作。
with操作

with语句可以用于访问文件,在语句块结束后会自动释放资源,不需要close()

with open('file','r+') as f:f.read()
with 使用示例:# 临时打开文件简单使用 file = open()
with open("file.txt") as file:data = file.read()print(data)# 语句块结束 file 被销毁

读写缓冲区

系统自动的在内存中为每一个正在使用的文件开辟一个空间,读写时都是先将文件内容加载到缓冲区,再进行读写。减少和磁盘的交互次数,提高了文件的读写效率。

  • 缓冲区设置

    类型 设置方法 注意事项
    系统自定义 buffering=-1
    行缓冲 buffering=1 当遇到\\n时会刷新缓冲
    指定缓冲区字节大小 buffering>1 必须以二进制方式打开
  • 刷新缓冲区条件

1. 缓冲区被写满
2. 程序执行结束或者文件对象被关闭
3. 程序中调用flush()函数 ,file_obj.flush()
"""
文件读写缓冲区
"""
# 设置行缓冲
# file = open("file.txt",'w',buffering=1)# 设置缓冲区大小  必须二进制打开
file = open("file.txt", 'wb', buffering=10)while True:msg = input(">>")if not msg:break# file.write(msg + "\\n")file.write(msg.encode())# file.flush() # 主动刷新  不要等到10个字节file.close()

文件偏移量

打开一个文件进行操作时系统会自动生成一个记录,记录每次读写操作时所处的文件位置,每次文件的读写操作都是从这个位置开始进行的。

注意:
1. r或者w方式打开,文件偏移量在文件开始位置
2. a方式打开,文件偏移量在文件结尾位置

文件偏移量控制

tell()
功能:获取文件偏移量大小(字节)
返回值:文件偏移量
seek(offset[,whence])
功能: 移动文件偏移量位置
参数:offset  代表相对于某个位置移动的字节数。负数表示向前移动,正数表示向后移动。whence 是基准位置的默认值为0,代表从文件开头算起,1从当前,2从末尾算起。注意:必须以二进制方式打开文件时,基准位置才能是1或者2    
"""
文件偏移量
"""# 可读可写
file = open("file.txt",'wb+')file.write("你好".encode())
file.flush() # 保证写入print("文件偏移量:",file.tell())# 控制文件偏移量
file.seek(-3,2)   #单个汉字占用三个字节,前面要是三的倍数,否则报错了
data = file.read()
print('内容:',data.decode())  #好# 如果文件偏移量到某个位置继续写会覆盖后面内容
file.seek(0,0)          #file.seek(1000,0)占用达到预留空间的目的
file.write(b"AAA")    #三个字节,将覆盖掉好字
file.seek(0,0)   #回到0位读
print(file.read().decode())   #AAA好
file.close()
"""
编写一个程序,循环不停的写入日志 (my.log)。
每2秒写入一行,要求每写入一行都要显示出来。
格式:  1. Mon Aug 30 17:00:08 20212. Mon Aug 30 17:00:10 20213. Mon Aug 30 17:13:22 20214. Mon Aug 30 17:13:24 2021
结束程序后(强行结束),重新运行要求继续往下写,
序号衔接
提示: time.ctime() 获取当前时间time.sleep() 间隔
"""
import time# 每行实时显示  a --> 文件偏移量结尾
log = open("my.log",'a+',buffering=1)log.seek(0,0) # a+需要文件偏移量放到开头,否则len(log.readlines())=0
n = len(log.readlines()) + 1   # 现有的行数 + 1
while True:msg = "%d. %s\\n"%(n,time.ctime())log.write(msg)time.sleep(2) # 时间间隔2sn += 1
"""
现有几个文本文件,路径放在一个列表里,比如
files = [
"../day01/1.txt",
"../day02/2.txt",
"../day03/3.txt"
]
编写一个程序,将这些文件合并为一个大文件叫
union.txt
"""files = [
"../day01/1.txt",
"../day02/2.txt",
"../day03/3.txt"
]union = open("union.txt", 'w')for file in files:  # 一次for是拷贝一个文件fr = open(file,'r')while True:  # 一个文件拷贝过程data = fr.read(1024)if not data:breakunion.write(data)fr.close()union.close()

os模块

os模块是Python标准库模块,包含了大量的文件处理函数。

os.path.getsize(file)    #获取文件大小
os.listdir(dir)	    #查看文件列表
os.path.exists(file)  #判断文件是否存在
os.remove(file)    #功能: 删除文件 
import osprint("文件大小:",os.path.getsize("file.txt"))
print("文件列表:",os.listdir("."))
print("文件是否存在:",os.path.exists("file.txt"))
os.remove("file.txt")

正则表达式re

类别 元字符
匹配字符 . [...] [^...] \\d \\D \\w \\W \\s \\S
匹配重复 * + ? {n} {m,n}
匹配位置 ^ $ \\b \\B
其他 `
  • 匹配任意(非)数字字符

\\d 匹配任意数字字符,\\D 匹配任意非数字字符

In : re.findall('\\d{1,5}',"Mysql: 3306, http:80")
Out: ['3306', '80']
  • 匹配任意(非)普通字符

\\w 匹配普通字符,\\W 匹配非普通字符。说明: 普通字符指数字,字母,下划线,汉字。

In : re.findall('\\w+',"server_port = 8888")
Out: ['server_port', '8888']
  • 匹配任意(非)空字符

\\s匹配空字符,\\S 匹配非空字符。说明:空字符指 空格 \\r \\n \\t \\v \\f 字符

In : re.findall('\\w+\\s+\\w+',"hello    world")
Out: ['hello    world']
  • 匹配(非)单词的边界位置

\\b 表示单词边界,\\B 表示非单词边界。说明:单词边界指数字字母(汉字)下划线与其他字符的交界位置。

In : re.findall(r'\\bis\\b',"This is a test.")
Out: ['is']
"""
正则表达式元字符学习
"""
import re# 普通字符匹配自身
result = re.findall("ab","abcdabcda")
print(result) 	#['ab', 'ab']# 或关系
result = re.findall("ab|bc","abcdbcda")
print(result) 	#['ab', 'bc']# 匹配任意一个字符
result = re.findall('张.丰',"张三丰,张四丰,张五丰")
print(result) 	#['张三丰', '张四丰', '张五丰']# 匹配字符集  
result = re.findall('[ !A-Z]',"How are you!")
print(result)	#['H', ' ', ' ', '!']  空格 叹号 A-Z#取反 [^ ]
result = re.findall('[^ !A-Z]',"How are you!")
print(result) #['o', 'w', 'a', 'r', 'e', 'y', 'o', 'u']# 匹配重复 0次到多次
result = re.findall('wo*',"wooooo~~w!")
print(result)	#['wooooo', 'w']# 1次到多次
result = re.findall('wo+',"wooooo~~w!")
print(result)	#['wooooo']# 0次或1次
result = re.findall('wo?',"wooooo~~w!")
print(result)	#['wo', 'w']
# 一定的次数区间
result = re.findall('wo{3}',"wooooo~~w!")
print(result)	#['wooo']
result = re.findall('wo{2,4}',"wooooo~~w!")
print(result)	#['woooo']
result = re.findall('1[3-9][0-9]{9}',"wooooo~~w!")
print(result)	#['woooo']# 匹配开头结尾位置
result = re.findall('^Jame',"Jame,hello")
print(result)result = re.findall('Jame$',"hi,Jame")
print(result)# 匹配数字\\d
result = re.findall('\\d{1,5}',"Mysql: 3306, http:80")
print(result)	#['3306', '80']
# 匹配非数字\\D
result = re.findall('\\D+',"Mysql: 3306, http:80")
print(result)	#['Mysql: ', ', http:']# 匹配普通字符  #普通字符指数字,字母,下划线,汉字
result = re.findall('\\w+',"server_port = 8888")
print(result)result = re.findall('\\W+',"server_port = 8888")
print(result)  #[' = ']# 匹配空字符 和非空字符
result = re.findall('\\w+\\s+\\w+',"hello    world")
print(result)  #['hello    world']result = re.findall('\\S+',"C119-lei&3 sddf daf")
print(result)  #['C119-lei&3', 'sddf', 'daf']# 单词边界位置
result = re.findall(r'\\bis',"This is a test.")
print(result)# 匹配特殊符号
result = re.findall("\\d+\\.?\\d*","3.14 78 0.618 5.12 20")
print(result)  #['3.14', '78', '0.618', '5.12', '20']# 贪婪 --》 非贪婪
result = re.findall("a\\w+c","abbbbcbbbbbc")
print(result)  #['abbbbcbbbbbc']result = re.findall("a\\w+?c","abbbbcbbbbbc")
print(result)   #['abbbbc']# 子组
data = "张三:1996,李四:1998,王五:1997"
result = re.findall("(张三):(\\d+)",data)
print(result)  #[('张三', '1996')]result = re.findall("张三:(\\d+)",data)
print(result)  #['1996']result = re.search("(ab)+","ababababab")
print(result.group()) # abababababresult = re.search("(?P<xing>王|李)\\w{1,3}","王者荣耀")
print(result.group()) # 王者荣耀

练习

import re# 匹配出其中大写字母开头的单词
result = re.findall('[A-Z][a-z]*', "How are you,Jame!,I am")
print(result)# 匹配出年月日
result = re.findall('[0-9]+', "今天是:2021-4-30")
print(result)  #['2021', '4', '30']# 匹配出数字
result = re.findall('-?[0-9]+', "-20°的天气,战士负重15Kg")
print(result)  #['-20', '15']# 匹配电话号码
result = re.findall(r'\\b1[3578][0-9]{9}\\b', "王总:13838384386,银行卡:693518345879556790")
print(result)  #['13838384386']# 匹配qq号码 6-11
result = re.findall('[1-9][0-9]{5,10}', "王总:4268858")
print(result)  #['4268858']# 验证一个用户注册的用户名是否由6-12位数字字母下划线构成
name = input("User:")
result = re.findall("^[_0-9a-zA-Z]{6,12}$",name)
print(result)
特殊字符: . * + ? ^ $ [] () {} | \\  匹配时需在其前加\\转义

正则表达式分组

以()建立正则表达式的内部分组,子组是正则表达式的一部分,可以作为内部整体操作对象。

  • 作用 : 可以被作为整体操作,改变元字符的操作对象
e.g.  改变 +号 重复的对象
In : re.search(r'(ab)+',"ababababab").group()
Out: 'ababababab'e.g. 改变 |号 操作对象
In : re.search(r'(王|李)\\w{1,3}',"王者荣耀").group()
Out: '王者荣耀'
  • 捕获组

捕获组本质也是一个子组,只不过拥有一个名称用以表达该子组的意义,这种有名称的子组即为捕获组。

格式:(?P<name>pattern)

e.g. 给子组命名为 "pig" #命名不影响最终匹配结果
In : re.search(r'(?P<pig>ab)+',"ababababab").group('pig')
Out: 'ab'
随堂练习:import re# 匹配特殊符号
result = re.findall('\\$\\d+', "日薪:$150")
print(result)# 匹配如下书名
result = re.findall('《.+?》', "《加油! @奥特曼》,《重生——美少女》,《biobio~ 奥利给》")
print(result)# 只要李蛋分数
result = re.findall('(李蛋):(\\d+)', "李逵:68,李蛋:75,李明:86")
print(result)# 匹配一下IP地址  192.168.4.6  (\\d{1,3}\\.){3}\\d{1,3}
result = re.search('(\\d{1,3}\\.?){4}', "IP:192.168.4.6")
print(result.group())
 re.findall(pattern,string)     #返回子组功能: 根据正则表达式匹配目标字符串内容参数: pattern  正则表达式string 目标字符串返回值: 匹配到的内容列表,如果正则表达式有子组则只能获取到子组对应的内容data = "张三:1996,李四:1998,王五:1997"
result = re.findall("张三:(\\d+)",data)
print(result)  #['1996']

 re.split(pattern,string,max)功能: 使用正则表达式匹配内容,切割目标字符串参数: pattern  正则表达式string 目标字符串max 最多切割几部分返回值: 切割后的内容列表string = "Lily:1998,Tom:1996"
# 使用## 替换匹配到的内容
result = re.sub("\\W+","##",string,2)
print(result)   #Lily##1998##Tom:1996# 使用正则表达式匹配到的内容分割字符串
result = re.split("\\W+",string,2)
print(result)  #['Lily', '1998', 'Tom:1996']# 获取每处匹配内容,但是有组的化只返回组对应的部分
result = re.findall("(\\w+):(\\d+)",string)
print(result)    #[('Lily', '1998'), ('Tom', '1996')]    

 re.sub(pattern,replace,string,count)功能: 使用一个字符串替换正则表达式匹配到的内容参数: pattern  正则表达式replace  替换的字符串string 目标字符串count  最多替换几处,默认替换全部返回值: 替换后的字符串

生成match对象

 re.finditer(pattern,string)功能: 根据正则表达式匹配目标字符串内容参数: pattern  正则表达式string 目标字符串返回值: 匹配结果的迭代器
re.match(pattern,string)
功能:匹配某个目标字符串开始位置
参数:pattern 正则string  目标字符串
返回值:匹配内容match object

re.search(pattern,string)
功能:匹配目标字符串第一个符合内容
参数:pattern 正则string  目标字符串
返回值:匹配内容match object

练习

import re
string = "Lily:1998,Tom:1996"
result = re.finditer("\\w+",string)
# 迭代取值
for item in result:print(item) # match 对象  <re.Match object; span=(0, 4), match='Lily'>print(item.group()) # 获取对应内容  #Lilyprint(item.span()) # 获取匹配内容对应的位置  (0, 4)# 匹配字符串开头位置内容
result = re.match("\\w+",string)
print(result.group())   #Lily# # 匹配第一处符合条件的内容
result = re.search("(\\w+):(?P<year>\\d+)",string)
print(result.group())  #Lily:1998
print(result.group(1)) # 只要第一组内容   Lily
print(result.group("year")) # 要year组内容  1998

match对象使用

  • span() 获取匹配内容的起止位置

  • group(n = 0)

    功能:获取match对象匹配内容

    参数:默认为0表示获取整个match对象内容,如果是序列号或者组名则表示获取对应子组内容

    返回值:匹配字符串

综合练习:基于 inet.log完成
编写程序,通过输入一个端口名称(每段首个单词)打印出这个端口描述信息中的address is 的值提示: 段落之间有空行端口名称是每段第一个单词思路: 根据输入的端口名称找到段落在段落中匹配目标import re#  生成器函数
def parg():file = open("inet.log")  # 读方式# 每次while循环data获取一段内容while True:data = ""for line in file:if line == "\\n":breakdata += lineif data:yield data # 对外提供一段内容else:return # 到了文件结尾def main():# 获取端口port = input("端口名称:")for data in parg():# 看一下每段首个单词是否为 porthead = re.match("\\S+",data).group() # 得到首个单词if head == port:address = re.search("([0-9a-f]{4}\\.?){3}",data).group()return addressreturn "Not Found"# 程序入口
print(main())
"""
基于 inet.log完成
编写程序,通过输入一个接口名称(每段首个单词)
打印出这个接口描述信息中的address is 的值提示: 段落之间有空行接口名称是每段第一个单词思路: 根据输入的接口名称找到段落在段落中匹配目标
"""
import re#  生成器 --》 每次得到一段内容
def get_parg():file = open("inet.log")# 每次循环获取一段内容while True:data = ''for line in file:if line == "\\n":break  # 遇到空行data += lineif data:yield data  # 提供一段内容else:file.close()returndef main():port = input("输入接口名称:")#  每次得到一段内容,验证这一段是否为想要的for info in get_parg():#  匹配一段首个单词head = re.match("\\S+", info).group()# 如果是需要的段落匹配目标内容if head == port:result = re.search("([0-9a-f]{4}\\.?){3}", info)if result:return result.group()else:return "Unknown"if __name__ == '__main__':print("找到的地址:",main())