首页 > 技术文章 > 字符编码,文件操作

gengfenglog 2020-12-24 21:44 原文

字符编码

只跟文本文件和字符串有关与视频文件、音频、图片文件等无关

什么是字符编码
由于计算机内部只识别二进制,但是用户在使用计算机的时候却可以看到各式各样的语言字符
字符编码:内部记录了人类字符与数字对应关系的数据

字符编码发展史

  • 1.一家独大

    • 美国:计算机最初是由美国人发明的,美国人为了能够让计算机识别英文字符
    • ASCII码:里面记录了英文字符与数字的对应关系, 用一个字节来表示对应关系
    英文字符-----》内存:ASCII格式的二进制-----》硬盘:ASCII格式的二进制
    """
      所有的英文字符和符号加起来其实不超过127
      之所以使用八位是为了后续发现新的语言
    """
    必须记住的对应关系有以下几组
    	       A-Z:65-90
                   a-z:97-122
                   0-9:48-57  
    ```​     
    
    
  • 2.群雄割据

    • 中国
      • 为了能够让计算机识别中文,我们需要发明另外一套编码表
      • GBK码:记录了英文中文与数字的对应关系,对于英文还是使用一个字节,对应中文使用两个字节甚至更多字节
    中文字符、英文字符-----》内存:gbk格式的二进制-----》硬盘:gbk格式的二进制
    
    """
      两个字节其实也不够表示出所有的中文
      遇到生僻字可能需要更多位来表示
    """
    
    • 日本
      • 为了能够让计算机识别日文,也需要发明一套编码表
      • shift_JIS码:记录了日文英文与数字的对应关系
    日文字符、英文字符-----》内存:shif-jis格式的二进制-----》硬盘:shif-jis格式的二进制
    
    • 韩国
      • 为了能够让计算机识别韩文,也需要发明一套编码表
        • Euc_kr码:记录了韩文英文与数字的对应关系
    韩文字符、英文字符-----》内存:Euc_kr格式的二进制-----》硬盘:Euc_kr格式的二进制
    
  • 3.天下一统

    • 为了能够实现不同国家之间的文本数据能够彼此无障碍交流需要对编码统一
      • unicode(万国字符) :统一使用两个及以上字符记录字符与数字的对应关系
      • utf8(万国字符编码的优化版本):将英文还是用一个字节存储,将中文使用三个字节或更多字节存储
"""
           		      编码                                                 编码
               万国字符--------》内存:unicode格式的二进制-------》硬盘:utf-8格式的二进制
         
中文字符、英文字符--------》内存:unicode格式的二进制-------》硬盘:gbk格式的二进制

日文字符、英文字符--------》内存:unicode格式的二进制-------》硬盘:shift-jis格式的二进制

韩文字符、英文字符--------》内存:unicode格式的二进制-------》硬盘:Euc_kr格式的二进制
"""  

 '''现在默认使用的编码是utf8'''

字符编码应用

  • 如何保证不乱码:

    文件当初以什么编码编的 打开的时候就以什么编码解

    • 存乱了:用utf-8编码存入硬盘

    • 读乱了:将读的编码改成与存的编码一致

  • python解释器不同版本的编码差异

# coding:gbk  
"""
python2.X内部使用的编码默认是ASCII,在python程序文件的第一行,必须是在第一行。
此行代码就是告诉python解释器在运行程序的时候不要再使用默认的字符编码表,要使用gbk
"""
细节:以后写python程序,字符串前面要加小u,涉及这个程序在python2的程序中也能运行,
python3不需要加,它的本质就是unicode
     s = u'你'
    
"""python3.X内部使用utf8"""    
  • 运行python程序的三个步骤:python test.py

    • 1、启动python解释器

    • 2、解释器将test.py的内容从硬盘读入内存

    • 3、解释器解释执行刚刚读入内存的代码,识别python语法,比如x = "你"

  • 编码与解码

- 编码
  - 将人类能够读懂的字符安装指定的编码转换成数字

          编码                  编码
字符---------------->unicode------------->其他编码

          解码                  解码
字符<---------------unicode<-------------其他编码

- 解码
  - 将数字按照指定的编码转换成人类能够读懂的字符
  
x = "你"
# encode 编码
print(x)
res = x.encode("utf8")     
print(type(res))  # bytes 在python中直接将该类型看成二进制数据即可

"""
强调:在python3里,只会将unicode格式的数字转成字符,其余编码格式的数字均不会转换成字符
"""
# decode 解码
print(res)
print(res.decode("utf8"))   



文件处理

  • 什么是文件

    文件其实是操作系统暴露给用户或者应用程序操作硬盘的快捷方式(接口)

  • 如何操作文件(三步走)

    • 打开文件:利用关键字open打开文件

    • 读/写:利用其它方法操作文件

    • 关闭文件

r"D:\a\b\c\d.txt"  # 绝对路径:从头开始的文件夹路径 \代表路径分隔符,加r是取消\的特殊意义
r"a\a.txt"  # 相对路径:相对当前运行程序所在的文件夹路径

# 回车与换行的来龙去脉
http://www.cnblogs.com/linhaifeng/articles/8477592.html
    
    
# 打开文件:open是指定你要打开文件的路径
open(r"a.txt", mode="rt",) 

print(f.read())  # read()只适用于比较小的文件

# 关闭文件
f.close()  # 释放操作系统的资源

"""
open(文件路径,读写模式,字符编码)
    文件路径与读写模式是必须的
    字符编码是可选的(有些模式需要编码)
"""

# with上下文管理(能够自动帮你关文件)
with open(r'a.txt','r',encoding='utf8') as f1:  # f1=open() f1.close()
    print(f1.read())
"""以后代码操作文件 推荐使用with语法"""

控制文件读写模式

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

  • w:只写模式(不可读,不存在则自动创建,存在则清空内容)

  • a:只追加写模式 (不可读,不存在则自动创建,存在则在文件末尾追加内容)

# r: 如果文件不存在则报错,如果存在则读取文件内所有的内容
with open(r'b.txt', mode='r',encoding='utf-8') as f:
  print(f.read())  # 不存在直接报错

with open(r'a.txt', mode='r', encoding='utf8') as f:
  print(f.read())  # 读取文件内所有的内容
  
  
# w: 如果文件不存在则自动创建空文档,如果文件存在则清空
with open(r'b.txt', mode='w', encoding='utf-8') as f:
  f.write("你好啊1\n")
  f.write("你好啊2\n")
  f.write("你好啊3\n")


# a: 如果文件不存在则创建空文档,如果存在则只追加内容
with open(r'c.txt',mode='a',encoding='utf-8') as f:
	f.write("你好啊1\n")
	f.write("你好啊2\n")
	f.write("你好啊3\n")

"""
小总结:
	我们所学习的r w a读写模式都只能操作文本文件
"""

文件操作方法

  • 1.读系列

# 掌握
f.read()  # 一次性读取文件内所有内容,光标移动到文件末尾
f.readline()  # 每次只读取一行内容,光标移动到第二行首部
f.readlines()  # 读取每一行内容,存放于列表中
f.readable()  # 文件是否可读


with open(r'a.txt', 'r', encoding='utf8') as f:
    print(f.read())  # 读取所有内容
    print(f.readline())  # 一次只读文件一行内容
    print(f.readlines())  # 读取文件所有的内容,组织成列表,每个元素是文件的每行内容
    print(f.readable())  # True 判断当前文件是否具备读的能力
	# print(f.writable())  # False
  • 2.写系列

# 掌握
f.write('天青色等烟雨\n而我在等你\n')  # 针对文本模式的写,需要自己写换行符
f.writelines(['天苍苍','野茫茫']) # 可以将列表中多个字符串元素全部写入
f.writable()  # 文件是否可写


with open(r'a.txt','w',encoding='utf8') as f:
    f.write('克服一切困难 奥利给!')  # 往文件内写入文本内容
   # f.write(123)  # 写入的内容必须是字符串类型
    f.writelines(['jason','kevin','tony'])  # 可以将列表中多个字符串元素全部写入
    print(f.writable())  # True 判断当前文件是否具备写的能力
   # print(f.readable())  # False
    
# 了解    
f.flush()  # 直接将内存内文件数据刷到硬盘,相当于ctrl+s,催促硬盘输入,其实是降低效率
f.closed   # 文件是否关闭
f.encoding  # 如果文件打开模式为b,则没有该属性
  • 3.文件优化操作

with open(r'a.txt', 'r', encoding='utf8') as f:
    # print(f.read())  # 一次性读取文件内所有的内容
    # print(f.read())  # 一次性读取文件内所有的内容
    # print(f.read())  # 一次性读取文件内所有的内容
  '''
    1、一次性读完之后,光标停留在了文件末尾,无法再次读取内容
    2、该方法在读取大文件的时候,可能会造成内存溢出的情况
              解决上述问题的策略就是逐行读取文件内容
    '''
    for line in f:  # 文件变量名f支持for循环,相当于一行行读取文件内容
        print(line)  # 催促硬盘输入,其实是降低效率
    '''以后涉及到多行文件内容的情况一般都是采用for循环读取'''

文件操作模式

  • t: 文本模式(控制读写的内容都是字符串类型(默认的))

    • 1、默认的模式:r w a >>> rt wt at
    • 2、该模式所有操作都是以字符串为基本单位(文本)
    • 3、该模式必须要指定encoding参数
    • 4、该模式只能操作文本文件
  • b:二进制模式(控制读写的内存都是bytes类型)

    • 1、该模式可以操作任意类型的文件
    • 2、该模式所有操作都是以bytes类型(二进制)基本单位
    • 3、该模式一定不要指定encoding参数
    • 4、该模式写入时也需要提供字节类型
# b模式:对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作
with open('c.txt',mode='rb') as f:
    res = f.read()
    # print(type(res))
    # print(res)
    print(res.decode('utf-8'))

with open('1.mp4',mode='rb') as f:
    print(f.read())
    for line in f:
        print(line)


with open('1.mp4',mode='rb') as src_f,open(r'D:\111111.mp4',mode='wb') as dst_f:
    for line in src_f:
        dst_f.write(line)

with open('d.txt',mode='wb') as f:
    msg = "你好"
    f.write(msg.encode('utf-8'))

控制文件光标的移动

被动控制文件内光标的移动

只有t模式下的read(n)的n代表的是字符个数,除此以外都是字节个数

with open(r'a.txt','rb') as f:
     # print(f.read())
     print(f.read(6).decode('utf8'))


with open(r'a.txt','r',encoding='utf8') as f:
    # print(f.read())
     print(f.read(4))

"""
read()  括号内可以放数字
    在t模式下表示字符个数
    在b模式下表示字节个数
英文字符统一使用一个bytes来表示
中文字符统一使用三个bytes来表示
"""

主动控制文件内光标的移动

f.seek(whence,offset)

  • offset表示位移量(移动的字节个数)
    • 始终是以字节为最小单位

      • 正数从左往右移动
      • 负数从右往左移动
    • whence表示模式,有三种:

      • 0、以文件开头为参照系(支持t、b两种模式)

      • 1、只支持b模式,以当前所在的位置为参考系

      • 2、只支持b模式,以文件末尾为参考系

注意:只有0模式可以在t下使用,1和2只能在b模式下使用

with open('a.txt',mode='rt',encoding='utf-8') as f:
    f.seek(3,0)
    f.seek(5,0)
    print(f.tell())  # 查看光标移动了多少个字节

with open('a.txt',mode='rb') as f:
    f.seek(3,1)
    f.seek(5,1)
    print(f.tell())  # 查看光标移动了多少个字节
    res=f.read()
    print(res.decode('utf-8'))

with open('a.txt',mode='rb') as f:
    f.seek(-3,2)
    print(f.tell())  # 查看光标移动了多少个字节
    f.seek(0,2)

文件的内容修改

方式一:覆盖

  • 1、先将文件内容全部读入内存

  • 2、在内存中完成修改

  • 3、将修改后的内容覆盖回原文件

ps: 耗费内存不耗费硬盘
with open('e.txt', mode="rt", encoding='utf-8') as f:
    data = f.read()

    
with open('e.txt',mode='wt',encoding='utf-8') as f1:
    f1.write(data.replace('egon','EGON'))

方式二:新建

  • 1、以读的方式打开原文件,然后以写的方式打开一个临时文件

  • 2、读原文件的一行内容到内存,然后在内存中修改完毕后再写入临时文件,循环往复直到全部改完

  • 3、删出原文件,将临时文件重命名为原文件名

ps: 耗费硬盘不耗费内存
import os

with open('e.txt',mode='rt',encoding='utf-8') as src_f,open('.e.txt.swp',mode='wt',encoding='utf-8') as dst_f:
    for line in src_f:
        dst_f.write(line.replace('EGON','egon'))

os.remove('e.txt')
os.rename('.e.txt.swp','e.txt')

知识强化

简易版本的注册、登陆功能

"""
普通要求(实现一次即可)
    	注册功能
        	获取用户名和密码组织成你喜欢的样子写入文件即可
      登陆功能
        	获取用户名和密码然后比对是否与文件内用户数据一致
""" 
# 注册功能
    # 1.获取用户用户名和密码
    username = input('username>>>:').strip()
    password = input('password>>>:').strip()
    # 2.将用户名和密码组织成一个字符串
    data = '%s|%s' % (username, password)
    # 3.文件操作 写入数据
    with open(r'userinfo.txt', 'w', encoding='utf8') as f:
        f.write(data)
    print('用户:%s 注册成功' % username)
 
# 登录功能
    # 1.获取用户名和密码(先使用input获取用户输入的数据 然后strip()移除首尾空格 之后赋值给变量名username)
    username = input('username>>>:').strip()
    password = input('password>>>:').strip()
    # 2.获取文件内真实的用户数据
    with open(r'userinfo.txt','r',encoding='utf8') as f:
        # 由于现在文件中只有一行内容 我们干脆直接读取完
        data = f.read()  # 'jason|jason123'
        '''with子代码执行完之后的结果是可以在外面使用的'''
    # 3.切割字符串获取真实的用户名和密码
    res = data.split('|')  # ['jason', 'jason123']
    '''这里也可以直接使用解压赋值的操作  real_name,real_pwd = data.split('|')'''
    # 4.比对数据
    if username == res[0] and password == res[1]:
        print('登录成功')
    else:
        print('用户名或密码错误')

进阶版本的注册、登陆功能

"""
拔高要求(代码整合)
    	多用户注册功能
        	1.获取用户名和密码组织成你喜欢的样子
    			2.判断文件内是否有该用户名存在
      		3.写入文件
      多用户登陆功能
        	1.获取用户名和密码
        	2.获取文件内真实的用户数据
        	3.然后比对是否与文件内用户数据一致
"""
while True:
    print("""
    1.用户注册
    2.用户登录
    """)
    choice = input('请输入您想要执行的功能编号>>>:').strip()
    if choice == '1':
        # 1.获取用户用户名和密码
        username = input('username>>>:').strip()
        password = input('password>>>:').strip()
        # 2.将用户名和密码组织成一个字符串
        data = '%s|%s\n' % (username, password)
        # 2.1.判断用户名是否已存在
        with open(r'userinfo.txt', 'r', encoding='utf8') as f1:
            # 2.2.循环读取每一行用户数据
            for line in f1:
                # 2.3.获取每一行数据中的用户名
                real_name, real_pwd = line.split('|')
                # 2.4.判断用户名是否一致
                if username == real_name:
                    print('用户名已存在')
                    break
            else:
                # 3.文件操作 写入数据
                with open(r'userinfo.txt', 'a', encoding='utf8') as f:
                    f.write(data)
                print('用户:%s 注册成功' % username)
    elif choice == '2':
        # 1.获取用户名和密码(先使用input获取用户输入的数据 然后strip()移除首尾空格 之后赋值给变量名username)
        username = input('username>>>:').strip()
        password = input('password>>>:').strip()
        # 2.获取文件内真实的用户数据
        with open(r'userinfo.txt', 'r', encoding='utf8') as f:
            for line in f:
                # 3.切割字符串获取真实的用户名和密码
                '''注意密码末尾有\n'''
                real_name, real_pwd = line.split('|')  # ['jason', 'jason123\n']
                # 4.比对用户名和密码是否正确
                if username == real_name and password == real_pwd.strip('\n'):
                    print('登录成功')
                    break
            else:
                print('用户名或密码错误')
    else:
        print('没有改功能 爱咋滴咋滴!')

最low版员工管理系统

"""
1.代码运行起来之后 可以循环提示用户有哪些功能编号可以选择(已完成)
2.查看指定用户数据(格式化输出美化一下),查看所有用户信息(已完成)
3.修改用户的薪资的待遇(一通百通) 修改用户名,年龄(已完成)
4.查看所有用户数据(for循环展示) (已完成)

"""

user_list = []
while True:
    print(' 用户管理系统 '.center(50, '*'))
    print("""
           1. 添加用户
           2. 查看用户
           3. 查看所有用户信息
           4. 删除用户
           5. 修改用户信息
           6. 退出系统
       """)
    choice = input('请输入您想要执行的功能编号>>>:').strip()
    if choice == '1':
        print(" 添加用户功能 ".center(16, '^'))
        user_id = input('请输入用户ID>>>:').strip()
        if user_id.isdigit():
            for i in user_list:
                if i['user_id'] == user_id:
                    print('该用户ID已存在')
                    break
            else:
                name = input('请输入用户名>>>:').strip()
                age = input('请输入年龄>>>:').strip()
                salary = input('请输入薪资>>>:').strip()
                user_dict = {}
                user_dict['user_id'] = user_id
                user_dict['name'] = name
                user_dict['age'] = age
                user_dict['salary'] = salary
                user_list.append(user_dict)
                print(user_list)
                print('用户:%s 添加成功' % name)
        else:
            print('ID请输入数字!')
    elif choice == '2':
        print(" 查看用户功能 ".center(16, '^'))
        if user_list:
            cmd = input('请输入要查看的用户ID>>>:').strip()
            if cmd.isdigit():
                for i in user_list:
                    if i['user_id'] == cmd:
                        print('''
            <------- 用户信息 ------->        
            |        ID:  %s     
            |       名字:  %s     
            |       年龄:  %s 岁    
            |       薪资:  %s k    
            <--------- end --------->                      
                        ''' % (i['user_id'], i['name'], i['age'], i['salary']))
                    else:
                        print('查看的用户不存在!')
                        break
            else:
                print('用户ID请输入数字!')
        else:
            print('暂无用户信息!')
    elif choice == '3':
        if user_list:
            print(' 所有用户信息 '.center(50, '~'))
            for i in user_list:
                print(''' | ID: %s | 名字: %s   年龄: %s   薪资: %s  ''' % (i['user_id'], i['name'], i['age'], i['salary']))
        else:
            print('暂无用户信息')
    elif choice == '4':
        print(" 删除用户功能 ".center(16, '^'))
        for i in user_list:
            print(''' | ID: %s | 名字: %s   年龄: %s   薪资: %s  ''' % (i['user_id'], i['name'], i['age'], i['salary']))
            cmd = input("请输入要删除的用户ID>>>:")
            if cmd.isdigit():
                if i['user_id'] == cmd:
                    user_list.remove(i)
                    print('用户已删除!')
                    break
                else:
                    print('要删除的用户不存在!')
            else:
                print('用户ID请输入数字!')
        else:
            print('暂无用户!')
    elif choice == "5":
        print('修改用户信息')
        for i in user_list:
            cmd = input('请输入要修改信息的用户ID>>>:').strip()
            if cmd.isdigit():
                if i['user_id'] == cmd:
                    print("""
                    1、修改用户名
                    2、修改用户年龄
                    3、调整用户薪资
                    """)
                    choice = input("请输入要修改的功能编号,按'q'退出>>>:").strip()
                    if choice == 'q' or choice == 'Q':
                        break
                    elif choice == '1':
                        new_name = input('请输入修改的薪资金额>>>:')
                        i['name'] = new_name
                        print('成功将用户名修改为:%s ' % new_name)
                    elif choice == '2':
                        new_age = input('请输入修改的薪资金额>>>:')
                        i['age'] = new_age
                        print('成功将用户的年龄修改为:%s ' % new_age)
                    elif choice == '3':
                        new_salsry = input('请输入修改的薪资金额>>>:')
                        i['salary'] = new_salsry
                        print('成功将用户的薪资调整为:%s k' % new_salsry)
                        break
                    else:
                        print('暂无此功能')
                        break
                else:
                    print('该用户不存在')
                    break
            else:
                print('用户ID请输入数字!')
        else:
            print('暂无用户!')
    elif choice == "6":
        print("您已退出系统".center(50, '='))
        break
    else:
        print("该功能不存在")

推荐阅读