一、文件概述
1. 什么是文件
计算机文件是一个存储在存储器上的数据的序列,可以包含任意的数据内容。
- 文本文件 有统一的字符编码
- 二进制文件 字符编码不统一(如:图片、视频文件等)
2. 字符编码
一个一个的小图片,字体文件 包含了这个字体类型的所有文字的图片。再给每个字符一个编号,
存储的时候,存这个编号。这个编号标就是字符编码。
2.1 ascii码
在计算机中,所有的数据在存储和运算时都要使用二进制数表示(因为计算机用高电平和低电平分别表示1和0),例如,像a、b、c、d这样的52个字母(包括大写)以及0、1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII编码,统一规定了上述常用符号用哪些二进制数来表示
ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符
1Byte = 8bit
1Kb = 1024B
1Mb = 1024Kb
1Gb = 1024Mb
1Tb = 1024Gb
一个字节 = 8位 8位二进制 0-255
一个ASCII码占用存储空间为1个字节
10000个ASCII码占用的内存大小为:10000 /1024 Kb
2.2 gb2312
ASCII不支持中文,为了支持计算机使用中文,推出gb2312.
GB2312编码适用于汉字处理、汉字通信等系统之间的信息交换,通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312。
基本集共收入汉字6763个和非汉字图形字符682个。
0-6763
2个字节来表示一个汉字
10000个中文所占的内存大小:10000*2B/1024
向上兼容ascii
2.3 gbk
GBK是GB2312的扩展,因为GB2312有些生僻字无法识别,所以开发了GBK编码,K就是扩展的意思
GBK 采用双字节表示,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 xx7F 一条线。总计 23940 个码位,共收入 21886 个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号 883 个。
2个字节表示一个汉字
向上兼容ascii
print('囙'.encode('gbk')) # 输出b'\x87\xe0'
print('囙'.encode('gb2312')) # 会报错
2.4 unicode
把世界上所有主流的编码都收纳进来。10w+
4个字节来表示一个字符
缺点:太大了,且内存变大
python3在内存里使用unicode
2.5 utf-8
根据字符编号动态的选择大小。
ascii 1个字节
中文 3个字节
UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。
ANSI码是指电脑用什么编码就用什么编码
3. 信息量
bit,一个位,表示一个二进制数 0,1
8bit叫一个Byte(字节)2的八次方 256
- 1024B = 1KB
- 1024KB = 1MB
- 1024MB = 1GB
- 1024GB = 1TB
0-255 之间的整数
4. 字节类型
Bytes表示二进制信息,它由单个字节构成的不可变序列。
字面量语法与字符串相似,加了一个前导符‘b’
只允许有ascii
超过了127的二进制值,使用十六进制来表示。
字符转换为对应的编码称为编码 A -> b'\x41'
根据字符编码转换为字符称为解码 b'\x41' -> A 解码
# ascii码字符编码,使用本身字母的字面量来表示。
res = 'abc123'.encode('ascii')
print(res, type(res)) # 输出b'abc123'
# gbk 用两个字节表示一个汉字,utf-8使用3个字节表示一个汉字
print('中'.encode('gbk'), type('中'.encode('gbk'))) # 输出b'\xd6\xd0'
print('中'.encode('utf-8'), type('中'.encode('utf-8'))) # 输出b'\xe4\xb8\xad'
# 解密
print(b'\xd6\xd0'.decode('gbk')) # 输出"中"
# 中‘’字在gbk编码中编码为54992
print(bin(54992))
print(hex(0b11010110))
不是所有的二进制信息都可以解码为字符。
用不一致的解码方式去打开对应的文件,会出现乱码:
notepad++打开一个记事本文件--编码--选择日文,并粘贴日文到记事本中--再使用其他编码方式,此时日文会变成乱码
二、python操作文件
- 打开文件
- 操作文件 (读 写)
- 关闭文件
1. open函数
open(file, mode='r', encoding=None)
mode
的取值:
字符 | 意义 |
---|---|
'r' |
文本读取(默认) |
'w' |
文本写入,并先清空文件(慎用),文件不存在则创建 |
'x' |
文本写,排它性创建,如果文件已存在则失败 |
'a' |
文本写,如果文件存在则在末尾追加,不存在则创建 |
和mode
组合的字符
字符 | 意义 |
---|---|
'b' |
二进制模式,例如:'rb'表示二进制读 |
't' |
文本模式(默认),例如:rt 一般省略t
|
'+' |
读取与写入,例如:'r+' 表示同时读写 |
2.读文本文件
在当前目录下创建一个名为 test.txt 的文本文件,(注意编码方式)文件中写入下面的内容:
静夜思
床前明月光,疑是地上霜。
举头望明月,低头思故乡。
2.1 操作基本步骤
# 打开文件 mode=rt,t可以省略
fb = open('test.txt', 'r', encoding='utf-8')
# 读取
content = fb.read()
print(content)
# 关闭文件
fb.close()
这种操作经常会忘记关闭文件句柄,造成资源浪费,所以处理文件是往往使用 with 语句进行上下文管理。
上面中的fb是文件句柄,通过fb去调用这个问题
何为句柄?
后面学习会遇到文件句柄、窗口句柄,下面是摘取一些网上的见解
句柄可以理解为就是一个id或者编码,可以凭借这个id对文件或者窗口进行标识了。
举例:就和你去饭店吃饭要排队,前台小姐给你分配了一个编号一样,然后她可以通过编号找到你。
在操作系统或者某种其他编程系统中,句柄就是这样的一个编号,用来对各种奇奇怪怪的东西编号,系统对你来说是一个黑箱,你只能通过这个编号向系统要东西。
句柄就是将直接调用改为间接调用,以避免每次调用的时候在库里搜寻,提高调用速度
2.2 with 上下文管理
with open('test.txt', 'r', encoding='utf-8') as fb:
content = fb.read()
print(content)
with 语句执行完毕会自动关闭文件句柄。
2.3 逐行读取
- read上面所使用的read会把文本的内容全部返回,很多时候会造成内存一下子变太大,而且有些数据不需要使用的,也会降低效率。下面介绍逐行读取文本内容的方法
在读取文本文件时,经常需要按行读取,文件对象提供了多种方法进行按行读取。
2.3.1readline
从文件中读取一行;如果 f.readline() 返回一个空的字符串,则表示已经到达了文件末尾
with open('test.txt', 'r', encoding='utf-8') as fb:
print(fb.readline())
print(fb.readline())
print(fb.readline())
print(fb.readline())
2.3.2readlines
以列表的形式返回文件中所有的行。
with open('test.txt', 'r', encoding='utf-8') as fb:
content = fb.readlines()
print(content)
2.3.3 迭代(推荐使用此方法)
要从文件中读取行,还可以循环遍历文件对象。这是内存高效,快速的,并简化代码:
with open('test.txt', 'r', encoding='utf-8') as fb:
for line in fb:
print(line)
2.4 相对路径与绝对路径
绝对路径
绝对路径是指定了文件在电脑中的具体位置,以 windows 电脑为例:
D:\Pycharm_workspace\day12file\读文本.py
相对路径
相对路径一般是指相对当前脚本的路径,比如上面的案例中的 test.txt 因为和当前脚本在同一个文件夹下,所以可以直接使用 test.txt 作为文件名来操作。
也可显式地表达当前路径 ./test.txt,
./ 表示当前目录
../ 表示上级目录,同理 ../../ 表示上上级目录,依此类推。
使用场景
相对路径:一般情况下项目本身的资源文件和脚本路径相对固定,为了不影响项目的移植性,必须使用相对路径。
相对路径:需要读取操作系统中固定位置的系统文件一般使用绝对路径。
3.读二进制文件
任何文件都可以以二进制读的方式打开,读取 test.txt 的二进制内容。
** 读取二进制文件时(也就是mode=rb时),不需要encoding参数**
# mode=rb,不需要encoding参数
with open('test.txt', 'rb') as fb:
content = fb.read()
print(content)
# 也可以逐行读取,以\n换行符标志
with open('test.txt', 'rb') as fb:
for line in fb:
print(line)
4. 写文本文件
4.1 清除写 w
mode=w 没有文件就创建,有就清除内容,小心使用
with open('test.txt', 'w', encoding='utf-8') as fb:
fb.write('锄禾\n')
fb.write('锄禾日当午,汗滴禾下土;\n')
fb.write('谁知盘中餐,粒粒皆辛苦。\n')
4.2 追加写 a
mode=a 追加到文件的最后
# mode=a 追加到文件的最后
with open('test.txt', 'a', encoding='utf-8') as fb:
fb.write('静夜思\n床前明月光,疑是地上霜;\n举头望明月,低头思故乡。\n')
4.3 排他写 x
在当前目录中创建文件 test.txt,存在则不创建,且会报错
with open('test2.txt', 'x', encoding='utf-8') as fb:
fb.write('1天空之城1')
FileExistsError: [Errno 17] File exists: 'test2.txt'
5.写二进制文件
在写模式后加 b 即是写二进制模式,这种模式下写入内容为字节数据。
例如:将爬到的图片二进制信息写入文件中。
import requests
url = 'https://pic.netbian.com/uploads/allimg/211115/232008-1636989608f1ca.jpg'
response = requests.get(url)
# print(response.content) # 打印出图片的二进制信息
with open('美女壁纸.jpg', 'wb') as f:
f.write(response.content)
6.读写文件
有时候需要能够同时读写文件,在模式后面加上 + 号即可给读模式添加写,给写模式添加读。
7.案例:python 处理解析 CSV 文件
读取csv文件并解析为嵌套列表
方法一:
实现思路:用readlines把列表的形式返回文件中所有的行,但是此时各个元素的末端有回车,利用for循环获取到每个元素,strip()把回车符去掉,再把每个元素(字符串)转换成列表
with open('鸢尾.csv', 'r', encoding='gbk') as fb:
content = fb.readlines()
for i in range(len(content)):
content[i] = content[i].strip()
content[i] = content[i].split(',')
方法二(推荐):
data = []
with open('鸢尾.csv', 'r', encoding='gbk') as fb:
for line in fb:
# 去掉每行末端的回车
line = line.strip()
data.append(line.split(','))
print(data)
# 将数据写为csv文件
with open('test.csv', 'w', encoding='utf-8') as f:
for item in data:
f.write(','.join(item) + '\n')
8.文件指针
open 函数返回的文件对象使用文件指针来记录当前在文件中的位置。
8.1 read 方法
在读模式下,使用文件对象的 read 方法可以读取文件的内容。它接收一个整数参数表示读取内容的大小,文本模式下表示字符数量,二进制模式下表示字节大小。
# 读取前面三个字节 '锄禾\n'
with open('test.txt', 'r', encoding='utf-8') as f:
content = f.read(3)
print(content)
with open('test.txt', 'rb') as f:
content = f.read(3)
print(content) #输出前面三个字节:b'\xe9\x94\x84',实际上就是一个锄字
'锄'.encode('utf-8') # 三个字节b'\xe9\x94\x84'
当以读的方式打开文件后文件指针指向文件开头,执行 read 操作之后,根据读取的数据大小指针移动到对应的位置。
8.2 tell 方法
文件对象的 tell 方法返回整数,表示文件指针距离文件开头的字节数。
with open('test.txt', 'r', encoding='utf-8') as f:
print(f.tell())
content = f.read(3)
print(content)
print(f.tell())
r 模式打开文件后文件指针指向文件开头,执行 read 操作之后,根据读取的数据大小指针移动到对应的位置。
with open('test.txt', 'a', encoding='utf-8') as f:
print(f.tell())
243
a 模式打开文件后文件指针指向文件末尾。
8.3 seek 方法
通过文件对象的 seek 方法可以移动文件句柄
seek 方法接收两个参数:
- offset 表示偏移指针的字节数
- whence 表示偏移参考,默认为 0
0 表示偏移参考文件的开头,offset 必须是 >=0 的整数
1 表示偏移参考当前位置,offset 可以是负数
2 表示偏移参考文件的结尾,offset 一般是负数
注意文本模式下只允许从文件的开头进行偏移,也即只支持 whence=0
with open('test.txt', 'r', encoding='utf-8') as f:
print(f.read(3))
# 跳转到文件开头
f.seek(0)
# 再读取第一个字
print(f.read(1))
锄禾
锄
with open('test.txt', 'rb') as f:
# 读取文件最后的10字节
f.seek(-10,2)
print(f.read())
b'\xe5\x8f\x88\xe7\x94\x9f\xef\xbc\x81\n'