8. 字符编码与文件操作

字符编码

三大核心硬件

CPU、内存、硬盘

所有软件都是运行硬件之上的,与运行软件相关的三大核心硬件为cpu、内存、硬盘,在程序运行时,产生的数据都是存放在内存中的

  1. 软件运行前,软件的代码及其相关数据都是存放于硬盘中的

  2. 任何软件的启动都是将数据从硬盘中读入内存,然后cpu从内存中取出指令并执行

  3. 软件运行过程中产生的数据最先都是存放于内存中的,若想永久保存软件产生的数据,则需要将数据由内存写入硬盘

文本编辑器获取文件内容的流程

  1. 启动一个文件编辑器(文本编辑器如nodepad++,pycharm,word)

  2. 文件编辑器会将文件内容从硬盘读入内存

  3. 文本编辑器会将刚刚读入内存中的内容显示到屏幕上

python解释器执行文件的流程

  1. 启动python解释器,此时就相当于启动了一个文本编辑器

  2. python解释器相当于文本编辑器,从硬盘上将test.py的内容读入到内存中

  3. python解释器解释执行刚刚读入的内存的内容,开始识别python语法

比较

​ 相同点:前两个阶段二者完全一致,都是将硬盘中文件的内容读入内存,详解如下
python解释器是解释执行文件内容的,因而python解释器具备读py文件的功能,这一点与文本编辑器一样

​ 不同点:在阶段3时,针对内存中读入的内容处理方式不同,详解如下
文本编辑器将文件内容读入内存后,是为了显示或者编辑,根本不去理会python的语法,而python解释器将文件内容读入内存后,可不是为了给你瞅一眼python代码写的啥,而是为了执行python代码、会识别python语法)

字符编码

简单的说,计算机存储的内容都是二进制的数字,但是人们在输入的时候都是一些人能够理解的字符,于是人们将字符和一些二进制数字进行一一对应,形成字符编码,在输入时,计算机能够根据对应关系将字符翻译成为二进制数字存储到内存中

字符编码发展史

  1. 一家独大——美国——英文字符、数字、符号——ASCII码——8位二进制(一个字节)数表示一个字母
  2. 诸侯割据——多国家——中文英文数字字符——GBK(国标)——16位二进制表示一个中文字符——日本——shift_jis编码——相互之间不能兼容
  3. 解决兼容问题——万国码unicode(定长)——中日韩英等多个国家——16位二进制表示——不仅要兼容各种语言,之前的编码表示的字符也要能用unicode表示出来(能作为一个中转,向下转换成各种编码)——但是各种老的编码之间不能互相转换(gbk是中文,不能转换成日文的shift_jis)
  4. 功能升级——utf-8(unicode transform format-8)——变长——1bytes表示英文字母,3bytes表示汉字

内存统一使用的编码是unicode,我们可以改变的是存入硬盘时字符的格式,简单地说就是我们在电脑上输入的内容都是转换成unicode格式存入内存,然后又转换成其它的格式(gbk、ASCII等等)存入硬盘

​ unicode编码无法进行直接进行存储和传输(网络传输),需要先转化成bytes类型(一种非unicode编码,和二进制相似,叫做字节文本,对于ASCII码,前面加上b就是bytes类型,如b'abc',对于非ASCII编码,显示的是十六进制)

文本编辑器对于字符编码的应用

文本文件存取编码乱码问题

​ 存乱如何解决:编码格式应该设计成支持文件内字符串的格式,就是格式要和文字的内容对应上

​ 取乱如何解决:文件以什么编码格式存入硬盘的,就应该以什么格式读取出来,简单的说就是修改编码

​ 一句话就是什么编码存的就什么编码取(相对硬盘存取来说的)

python解释器默认读文件的编码

​ python3的默认编码是utf-8,python2的默认编码是ASCII

​ 用户指定文件的编码: # coding:gbk

​ 需要注意的是,在解释器读取# coding:gbk之前,还是使用默认的编码进行读取的,在读取了# coding:gbk之后,才改成指定的编码(gbk)

​ 将变量存入内存的过程:在python3中会将str类型默认直接存成unicode,所以永远不会出现乱码的问题,但是python2是直接指定存储的编码,例如x=u'nihao',u就是直接指定存成unicode,所以为了不乱码,python2写程序的时候直接在字符串的前面加上u

了解知识

python2中的字符串有两种类型,一种是unicode(上面的加u的写法),另一种是str,就是直接写x='上',如果不指定类型(不加u),就是使用指定的编码方式,但是在python3中,取消了这个分类,统一存储成unicode

GBK变长存储字符的原理

8位表示一个字符串,实际上只有后7位是表示字符的编码,第一位是表示的标志位,如果是中文字符标记为1,英文字符的话就标记为0,这样的话就能实现变长存储了

编码与解码

内存中存储的数据是unicode

编码是指将unicode转换成其他的编码

​ x.encode('gbk')

解码是指将其他类型的编码装换成unicode

​ x.decode('gbk')

文件介绍

文件相关概念

文件:文件就是操作系统提供给用户/应用程序操作硬盘的一种虚拟的概念/接口

作用:基于文件,用户或者应用程序可以通过文件将数据永久保存在硬盘上,操作文件就是操作硬盘

用户/应用程序直接操作的是文件,对文件进行的所有操作,都是在向操作系统发送系统调用,然后再有操作系统将系统调用转换成具体的文件操作

# 1. 打开文件,得到文件句柄并赋值给一个变量
f=open('a.txt','r',encoding='utf-8')  # 默认打开模式就为r

# 2. 通过句柄对文件进行操作
data=f.read()

# 3. 关闭文件
f.close()

方式:open('文件路径','模式')

文件资源回收

打开一个文件包含两部分资源:操作系统级打开的文件+应用程序的变量。在操作完毕一个文件时,必须把与该文件的这两部分资源一个不落地回收,回收方法为:
1、f.close() #回收操作系统级打开的文件
2、del f #回收应用程序级的变量

其中del f一定要发生在f.close()之后,否则就会导致操作系统打开的文件还没有关闭,白白占用资源,
而python自动的垃圾回收机制决定了我们无需考虑del f,这就要求我们,在操作完毕文件后,一定要记住f.close()

对于经常忘记使用f.close()的使用者,python创建了with关键字来帮我们管理上下文
with open('a.txt','w') as f:
    pass
 
with open('a.txt','r') as read_f,open('b.txt','w') as write_f:
    data=read_f.read()
    write_f.write(data)

控制文件读写内容的模式:t和b,t和b不能单独使用,必须和r/w/a模式连用

  • t模式:以文本进行读取:默认模式,以文本的模式读写文本

    ​ 读写文本的时候都是以str(unicode编码)的为单位的

    ​ 只针对文本文件,不包括图片,视频等文件格式

    ​ 必须指定字符编码(encoding参数)

  • b模式(binary模式,二进制模式):不完全是二进制,是一种类似于二进制的编码方式,称为bytes类型

    ​ 读写都是以bytes类型为单位

    ​ 可以针对所有文件(视频图片等)

    ​ 一定不能指定字符编码(encoding参数)

t模式使用更加方便,但是只限于文本对象,b模式比较通用,不过需要bytes类型进行中间的转换

# 文件操作基本流程
# 1.打开文件
f=open(r'C:\a\b\c.py',mode='rt',encoding='utf-8')  # f叫文件句柄(也叫文件对象),指定文件的打开模式(rt就是只读,t是text,指的是对文件进行操作,可以不写,默认就是t模式)和文件的编码(不写的情况下默认是操作系统的编码方式,linux和mac是utf-8,windows系统默认是gbk)文件编码指的是将文件从硬盘中取出来放入内存时,指定将二进制代码按照那种方式转换成unicode存入内存
# 解决文件路径中分隔符\转义的问题
# a. 一种是上面的这种,在前面加一个r,表示字符串中的斜杠不进行转义
f=open(r'C:\a\b\c.py',mode='r',encoding='utf-8')
# b. 一种是使用反斜杠
f=open('C:/a/b/c.py',mode='r',encoding='utf-8')
# 2.操作文件:读写文件就是应用程序将对文件的读写请求向操作系统发送系统调用,有操作系统控制硬盘将文件内容读入内存
res=f.read()  # 就是进行IO操作,在将文件从硬盘中读入内存(读写文件的结果都是字符串),read(100),一次只读取100个字符的长度
# 3.关闭文件
f.close()  # 回收操作系统的资源,此时的f还存在,在内存中存着
### 这一步是很必要的,因为操作系统同时打开的文件数是有限的,当多个人同时操作的时候,关闭文件就很必要,不然会影响他人的操作

# with上下文管理(就打开文件的另一种方式)
with open('C:/a/b/c.py',mode='r',encoding='utf-8') as f:
res=f.read()
# 不需要进行close,在不对文件进行操作的一段时间之后,文件句柄会自动关闭,这样就不用人为关闭文件句柄了

控制文件读写操作的模式

r只读模式,默认模式,文件必须存在,不存在则抛出异常

w只写模式,不可读;不存在则创建;存在则清空内容

a追加模式,不存在则创建;存在则只追加内容

+方法:r+、w+、a+

# 文件操作模式详解
# r模式:只读模式,也是默认的操作模式,不可写(会报错)
with open('a.txt',mode='r',encoding='utf-8') as f:
# 文件不存在时会报错,当文件存在时,文件指针跳到最先开始的部分,从头开始读取文件的内容
res1=f.read()  # 将文件一次性读取全部,存到内存中
f.readline()  # 读一行,不继续往下读
f.readlines()  # 读所有的行,每行以换行符隔开,形成列表
          
# w模式:只写模式,不可读
# 文件不存在时,会创建一个空白文件,文件名是你指定的名,当文件存在时,w模式会清空文件,指针会位于开始位置
with open('a.txt',mode='w',encoding='utf-8') as f:
res2=f.write() 
f.writelines([1,2,3,4,5,6,7])  # 将列表中的所有元素写成一行写到文件中
### w模式打开文件连续的写文件内容(不关闭文件句柄),由于光标的关系不会对文档进行清空,换句话说,文件打开时会将光标置于开头处
# 用于写新文件

# a模式:只追加写模式,不可读
with open('a.txt',mode='at',encoding='utf-8') as f:
res3=f.write()
# 文件不存在时会创建空文档,当文件存在时会将文件指针直接跳到末尾,在原有内容的末尾追加你写的内容
# a模式常用于老文件(记录日志、用户注册信息)

####案例:登录时的身份验证####
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='') # egon:123\n
         username,password=line.strip().split(':')
         if inp_username == username and inp_password == password:
             print('login successfull')
             break
     else:
         print('账号或密码错误')
####案例:复制拷贝文件####
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:
    # 方式1: 
    while 1 :
        res=f1.read(1024)  # 能够自己控制每次读取的数据量
        if len(res)==0:
          break
    # 方式2:
    for line in f1:  # for循环比较节省空间,但是当一行的内容很多的时候,这种方式就不推荐了
        f1.write(res)


####案例:注册信息####
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('db.txt',mode='r+',encoding='utf-8') as f:
# 读和覆盖的写(关注指针的位置,指针开始的位置开始覆盖的写)
# w+:可读可写(关注指针的位置,从指针开始的位置开始读)          
# a+:可读可写(同样关注指针的位置)

文件补充

  1. 文件模式补充

    x模式

    只写模式(不存在则创建,存在则报错)

    with open('db.txt',mode='x',encoding='utf-8') as f:

  2. 文件操作的其他方法

    with open('db.txt',mode='w',encoding='utf-8') as f:
    	f.write('哈哈哈')
        f.flush()  # 刷新,原理是当写入的内容很少时,内容不会立刻写到文件中,而是等内容满足一定的量时才会写入,这时就可以进行flush操作,将内容写到文件中
        f.readable()  # 判断文件句柄是否可读
        f.writable()  # 判断文件是否可读
        f.encoding()  # 显示文件的编码是什么
        f.name()  # 显示文件的名称
    
  3. 指针的移动

    # 指针的移动单位是字节,英文数字1字节,汉字3字节
    f.seek(n,模式)  # n指的是移动的字节个数,模式为0,参照的是文件的开头位置,1参照的是指针现在的位置,2参照物是文件末尾,当模式为2时,前面的n一般是负数,表示倒着移动
    f.tell()  # 显示文件指针当前的位置
    
    ### 0模式是在t模式下使用,1和2是在b模式下使用
    
    #####f.seek的应用####
    with open('db.txt',mode='rb',encoding='utf-8') as f:
        # 将指针调到文件末尾,这里使用read方法并不合适,因为耗内存
        f.seek(0,2)
        while 1 :
            line=f.readline()
            if len(line)==0:
                time.sleep(0.3)
            else:
                print(line.decode('utf-8'),end='')
    
  4. 文件修改的两种方式

    ​ 文件修改的时候,文本编辑器会将硬盘的文件取出放在内存中,将内存中的文件内容进行修改,当保存修改的时候,会将内存中的文件存入硬盘中,将原来的文件内容进行覆盖

    # 方式一:
    with open('db.txt',mode='rt',encoding='utf-8') as f:
        res=f.read()  # 占用内存过大
        # 这里可以对文件内容进行一些操作,比如进行替换什么的
    with open('db.txt',mode='wt',encoding='utf-8') as f1:  # 需要注意的是先r模式打开文件句柄,再w模式打开文件句柄会将文件的内容清空
        f1.write(res)
    
    # 方式二:
    import os
    with open('d.txt',mode='rt',encoding='utf-8') as f,\
        with open('.d.txt.swap',mode='wt',encoding='utf-8') as f1:
        for line in f:
            f1.write(line.replace('nihao','hello'))
    os.remove('d.txt')
    os.rename('.d.txt.swap','d.txt')
    

    对比:

    方式一浪费的是内存空间(文本编辑器),方式二浪费的是硬盘空间(程序使用)

posted @ 2021-07-20 00:20  奇点^  阅读(199)  评论(0)    收藏  举报