文件处理
-
文件分类
- 文本文件:打开后会自动解码为字符,如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
# 每次取一行abandonment
for 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:
break
fw.write(data)
fr.close()
fw.close()
copy("/home/b.jpg")
close关闭文件
打开一个文件后我们就可以通过文件对象对文件进行操作了,当操作结束后可以关闭文件操作
file_object.close()
- 可以销毁对象节省资源,(当然如果不关闭程序结束后对象也会被销毁)。
- 防止后面对这个对象的误操作。
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 2021
2. Mon Aug 30 17:00:10 2021
3. Mon Aug 30 17:13:22 2021
4. 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) # 时间间隔2s
n += 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:
break
union.write(data)
fr.close()
union.close()
os模块
os模块是Python标准库模块,包含了大量的文件处理函数。
os.path.getsize(file) #获取文件大小
os.listdir(dir) #查看文件列表
os.path.exists(file) #判断文件是否存在
os.remove(file) #功能: 删除文件
import os
print("文件大小:",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()) # ababababab
result = 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()) # 获取对应内容 #Lily
print(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":
break
data += line
if data:
yield data # 对外提供一段内容
else:
return # 到了文件结尾
def main():
# 获取端口
port = input("端口名称:")
for data in parg():
# 看一下每段首个单词是否为 port
head = re.match("\S+",data).group() # 得到首个单词
if head == port:
address = re.search("([0-9a-f]{4}\.?){3}",data).group()
return address
return "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 += line
if data:
yield data # 提供一段内容
else:
file.close()
return
def 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())