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
    # 每次取一行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()
  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 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())

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值