Python字符编码及文件相关
一、字符编码相关
1.介绍:
常见编码有ascii编码(美国),GBK编码(中国),shift_JIS编码(日本),unicode(统一编码)等。
python 2.x默认的字符编码是ASCII,默认的文件编码也是ASCII。
python 3.x默认的字符编码是unicode,默认的文件编码是utf-8。
Python支持中文的编码:utf-8、gbk和gb2312。uft-8为国际通用,常用有数据库、编写代码。gbk如windows的cmd使用。
2.中文乱码问题:
无论以什么编码在内存里显示字符,存到硬盘上都是二进制,所以编码不对,程序就会出错。存到硬盘上时是以何种编码存的,再从硬盘上读出来时,就必须以何种编码读,要不然就会出现乱码问题。 由于所有的系统、编程语言都默认支持unicode。所以只需要把unicode作为编码解码的中间媒介。
转码的过程:
源有编码 -> unicode编码 -> 目的编码
decode("UTF-8") 解码 --> unicode --> encode("gbk") 编码
3.Python2.x与Python3.x区别
在python2.x中有两种字符串类型str和unicode
例如:当python解释器执行到产生字符串的代码时(例如s='学习'),会申请新的内存地址,然后将'学习'编码成文件开头指定的编码格式要想看s在内存中的真实格式,可以将其放入列表中再打印,而 不要直接打印,因为直接print()会自动转换编码
>>>s = ”学习“ >>>print s 学习 >>>s # 字节类型 '\xd1\xa7\xcf\xb0'
unicode类型:
当python解释器执行到产生字符串的代码时(例如s=u'林'),会申请新的内存地址,然后将'林'以unicode的格式存放到新的内存空间中,所以s只能encode,不能decode转换在字符前加u‘,Windows默 认的编码是“gbk”格式,而pycharm默认的是--**--Unicode--**--utf-8格式,编码如果不统一,解码时就会显示乱码,或者会报错,保证不乱码的前提是编码要统一,不 然就进行强制转换
#coding:gbk x=u'上' #等同于 x='上'.decode('gbk') y=u'下' #等同于 y='下'.decode('gbk') print([x,y]) #[u'\u4e0a', u'\u4e0b'] print(type(x),type(y)) #(<type 'unicode'>, <type 'unicode'>)
Python3中str都是Unicode编码的,所以Python3中的str类型的数据可以编码成其他字符编码的格式,编码的结果为bytes类型。
# coding:gbk x = '上' # 当程序执行时,无需加u,'上'也会被以Unicode形式保存新的内存空间中, print(f"type(x): {type(x)}") # <class 'str'> # x可以直接encode成任意编码格式 print(f"x.encode('gbk'): {x.encode('gbk')}") # b'\xc9\xcf' print(f"type(x.encode('gbk')): {type(x.encode('gbk'))}") # <class 'bytes'>
二、文件操作
基本操作
1、打开文件
得到文件句柄,可以复制给一个变量
# windows路径分隔符问题 # open('C:\a.txt\nb\c\d.txt') # 解决方案一:推荐 # open(r'C:\a.txt\nb\c\d.txt') # 解决方案二: # open('C:/a.txt/nb/c/d.txt') f=open(r'aaa/a.txt',mode='rt') # f的值是一种变量,占用的是应用程序的内存空间,此时打开文件 # print(f) # x=int(10)
2、操作文件
文件的操作:读、写、修改、添加(f.read、f.close、f.open)要定义打开的模式不然默认为 mode='r' )# r取消转义。读/写文件,应用程序对文件的读写请求都是在向操作系统发送
系统调用,然后 由操作系统控制硬盘把输入读入内存、或者写入硬盘
res=f.read() print(type(res)) # print(res) #关闭文件 f.close() # 回收操作系统资源
3、with上下文管理
# 文件对象又称为文件句柄 # with open('a.txt',mode='rt') as f1: #等同于f1=open('a.txt',mode='rt') # res=f1.read() # print(res) with open('a.txt',mode='rt') as f1,\ open('b.txt',mode='rt') as f2: res1=f1.read() res2=f2.read() print(res1) print(res2) # f1.close() # f2.close()
4、指定字符编码
''' 强调:t和b不能单独使用,必须跟r/w/a连用,r代表read,w代表write,a代表追加写模式,不会创造新的文件,也不会清除源文件内容 t文本(默认的模式) 1、读写都以str(unicode)为单位的 2、文本文件 3、必须指定encoding='utf-8' ''' # 没有指定encoding参数操作系统会使用自己默认的编码 # linux系统默认utf-8 # windows系统默认gbk with open('c.txt',mode='rt',encoding='utf-8') as f: res=f.read() # t模式会将f.read()读出的结果解码成unicode print(res,type(res)) # 内存:utf-8格式的二进制-----解码-----》unicode # 硬盘(c.txt内容:utf-8格式的二进制)
5、文件操作模式
#1. 打开文件的模式有(默认为文本模式): r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】 w,只写模式【不可读;不存在则创建;存在则清空内容】 a, 之追加写模式【不可读;不存在则创建;存在则只追加内容】 #2.对于文本文件,我们用t模式,读写都以str(unicode)为单位的.一定要指定encoding='utf-8' rt wt at #3. 对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式) rb wb ab 注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码 #4. 了解部分 "+" 表示可以同时读写某个文件 r+, 读写【可读,可写】 w+,写读【可读,可写】 a+, 写读【可读,可写】 x, 只写模式【不可读;不存在则创建,存在则报错】 x+ ,写读【可读,可写】 xb
案例:
# 以t模式为基础进行内存操作 # 1、r(默认的操作模式):只读模式,当文件不存在时报错,当文件存在时文件指针跳到开始位置 inp_username=input('your name>>: ').strip() inp_password=input('your password>>: ').strip() with open('user.txt',mode='rt',encoding='utf-8') as f: for line in f: print(line,end='') # aaa:123\n username,password=line.strip().split(':') if inp_username == username and inp_password == password: print('login successfull') break else: print('账号或密码错误') # 2、w:只写模式,当文件不存在时会创建空文件,当文件存在会清空文件,指针位于开始位置#with open('d.txt',mode='wt',encoding='utf-8') as f: # f.write('擦勒\n') # 强调1: # 在以w模式打开文件没有关闭的情况下,连续写入,新的内容总是跟在旧的之后 # with open('d.txt',mode='wt',encoding='utf-8') as f: # f.write('1\n') # f.write('2\n') # f.write('3\n') # 强调2: # 如果重新以w模式打开文件,则会清空文件内容 # with open('d.txt',mode='wt',encoding='utf-8') as f: # f.write('擦勒1\n') # with open('d.txt',mode='wt',encoding='utf-8') as f: # f.write('擦勒2\n') # with open('d.txt',mode='wt',encoding='utf-8') as f: # f.write('擦勒3\n') #创建新文件,并读取 # src_file=input('源文件路径>>: ').strip() # dst_file=input('源文件路径>>: ').strip() # with open(r'{}'.format(src_file),mode='rt',encoding='utf-8') as f1,\ # open(r'{}'.format(dst_file),mode='wt',encoding='utf-8') as f2: # res=f1.read() # f2.write(res) # 3、a:只追加写,在文件不存在时会创建空文档,在文件存在时文件指针会直接调到末尾 # with open('e.txt',mode='at',encoding='utf-8') as f: # # f.read() # 报错,不能读 # f.write('11\n') # f.write('22\n') # f.write('33\n') # 强调 w 模式与 a 模式的异同: # 1 相同点:在打开的文件不关闭的情况下,连续的写入,新写的内容总会跟在前写的内容之后 # 2 不同点:以 a 模式重新打开文件,不会清空原文件内容,会将文件指针直接移动到文件末尾,新写的内容永远写在最后 # 案例:a模式用来在原有的文件内存的基础之上写入新的内容,比如记录日志、注册 # 注册功能 # name=input('your name>>: ') # pwd=input('your name>>: ') # with open('db.txt',mode='at',encoding='utf-8') as f: # f.write('{}:{}\n'.format(name,pwd)) # 了解:+不能单独使用,必须配合r、w、a # with open('g.txt',mode='rt+',encoding='utf-8') as f: # # print(f.read()) # f.write('中国') # with open('g.txt',mode='w+t',encoding='utf-8') as f: # f.write('111\n') # f.write('222\n') # f.write('333\n') # print('====>',f.read()) # # with open('g.txt',mode='a+t',encoding='utf-8') as f: # print(f.read()) # f.write('444\n') # f.write('5555\n') # print(f.read())
6、文件操作方法
f.read() #读取所有内容,光标移动到文件末尾 f.readline() #读取一行内容,光标移动到第二行首部 ,一次读一行 f.readlines() #读取每一行内容,存放于列表中 f.write('1111\n222\n') #针对文本模式的写,需要自己写换行符 f.write('1111\n222\n'.encode('utf-8')) #针对b模式的写,需要自己写换行符 f.writelines(['333\n','444\n']) #文件模式 f.writelines([bytes('333\n',encoding='utf-8'),'444\n'.encode('utf-8')]) #b模式 f.readable() #文件是否可读 f.writable() #文件是否可读 f.closed #文件是否关闭 f.encoding #如果文件打开模式为b,则没有该属性 f.flush() #立刻将文件内容从内存刷到硬盘 f.name
7、文件光标的移动
7.1: read(3):
1. 文件打开方式为文本模式时,代表读取3个字符
2. 文件打开方式为b模式时,代表读取3个字节
7.2: 其余的文件内光标移动都是以字节为单位如seek,tell,truncate(截断)
注意:
1. seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的
#f.seek(n,模式):n指的是移动的字节个数 # 模式: # 模式0:参照物是文件开头位置 # f.seek(9,0) # f.seek(3,0) # 3 # 模式1:参照物是当前指针所在位置 # f.seek(9,1) # f.seek(3,1) # 12 # 模式2:参照物是文件末尾位置,应该倒着移动 # f.seek(-9,2) # 3 # f.seek(-3,2) # 9 # 强调:只有0模式可以在t下使用,1、2必须在b模式下用
2.f.tell() # 获取文件指针当前位置
with open('aaa.txt',mode='rb') as f: # f.seek(9,0) # f.seek(3,0) # 3 # # print(f.tell()) # f.seek(4,0) # res=f.read() # print(res.decode('utf-8'))
3. truncate是截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下测试效果
例子:
with open (r'E:\PY\venv\01.txt',encoding='utf-8',mode='rt') as f: # 在rt模式下 read内的数字 表示的是字符的个数 print(f.read(11)) with open(r'E:\PY\venv\01.txt', encoding='utf-8', mode='rb') as f: ser=f.read(11) # rb 模式读取的是字节数 print(ser) # print(ser.decode('utf-8')) # f.seek(offset.whence) # offset:相对偏移量,光标的移动位数.相对偏移量可以为负数,代表从文末开始读取。为正数时代表从文始开始读取 # whence:'0,1,2' # 0:参照文件的开头 t和b 都可以使用 # 1:参照光标所在的当前位置 只能在b模式下用 # 2: 参照文件末尾 只能在b模式下使用
with open (r'E:\PY\venv\01.txt',mode='rb') as f: 不需要转成 encoding='utf-8' print(f.read(1)) set=f.seek(6,0) # 一个文字对应3个字节,一个英文对应一个字节 print(set) print(f.seek(6,0)) #rb模式下的都是移动字节数,出了r模式外 print(f.seek(0,0)) print(f.read(1))
print(f.seek(-4,1))
#添加文件
with open(r'test','r+',encoding='utf-8') as f:
# f.seek(6,0)
# f.write('过'),在第6个字符后面加"过"
8、文件修改:
''' 方式一: 1.先将数据由硬盘读到内存(读文件) 2.在内存中完成修改(字符串的替换) 3.再覆盖原来的内容(写文件) ''' with open(r'test02.txt','w',encoding='utf-8') as f: res = data.replace('egon','jason') # 直接替换 print(data) f.write(res) #优点:任意时间硬盘上只有一个文件 不会占用过多硬盘空间 #缺点:当文件过大的情况下,可能会造成内存溢出 ''' 方式二 # 创建一个新文件 # 循环读取老文件内容到内存进行修改 将修改好的内容写到新文件中 # 将老文件删除 将新文件的名字改成老文件名 ''' import os with open(r'test02.txt','r',encoding='utf-8') as read_f,\ open(r'test02.swap','a',encoding='utf-8') as write_f: for line in read_f: new_line = line.replace('jason','egon') write_f.write(new_line) ## 添加新内容 os.remove('test02.txt') os.rename('test02.swap','test02.txt') ## 重命名 #优点:内存中始终只有一行内容 不占内存 #缺点:再某一时刻硬盘上会同时存在两个文件
补充:文件模式之x模式
x模式(控制文件操作的模式)-》了解 x, 只写模式【不可读;不存在则创建,存在则报错】 """ # with open('a.txt',mode='x',encoding='utf-8') as f: # pass # with open('c.txt',mode='x',encoding='utf-8') as f: # f.read() with open('d.txt', mode='x', encoding='utf-8') as f: f.write('哈哈哈\n')
文件拷贝工具:减少内存占用
src_file=input('源文件路径>>: ').strip() dst_file=input('源文件路径>>: ').strip() with open(r'{}'.format(src_file),mode='rb') as f1,\ open(r'{}'.format(dst_file),mode='wb') as f2: # res=f1.read() # 内存占用过大 # f2.write(res) for line in f1: f2.write(line)
循环读取文件:两种方式
#方式一:自己控制每次读取的数据的数据量 # with open(r'test.jpg',mode='rb') as f: # while True: # res=f.read(1024) # 1024 # if len(res) == 0: # break # print(len(res)) # 方式二:以行为单位读,当一行内容过长时会导致一次性读入内容的数据量过大 # with open(r'g.txt',mode='rt',encoding='utf-8') as f: # for line in f: # print(len(line),line) # with open(r'g.txt',mode='rb') as f: # for line in f: # print(line) # with open(r'test.jpg',mode='rb') as f: # for line in f: # print(line)