今日内容:

    1.字符编码: 人识别的语言与机器识别的语言转化的媒介 *****
    2.字符与字节: 字符占多少字节,字符串转化 ***
    3.文件操作: 操作硬盘中的一块区域:读写操作  *****

一、文本编辑器存取文件的原理

1、打开编辑器就打开了启动了一个进程,是在内存中的,所以,用编辑器编写的内容也都是存放与内存中的,断电后数据丢失
2、要想永久保存,需要点击保存按钮:编辑器把内存的数据刷到了硬盘上。
3、在我们编写一个py文件(没有执行),跟编写其他文件没有任何区别,都只是在编写一堆字符而已

二、python解释器执行py文件的原理

第一阶段:python解释器启动,此时就相当于启动了一个文本编辑器
第二阶段:python解释器相当于文本编辑器,去打开test.py文件,从硬盘上将test.py的文件内容读入到内存中(小复习:pyhon的解释性,决定了解释器只关心文件内容,不关心文件后缀名)
第三阶段:python解释器解释执行刚刚加载到内存中test.py的代码( ps:在该阶段,即真正执行代码时,才会识别python的语法,执行文件内代码,当执行到name="egon"时,会开辟内存空间存放字符串"egon")
python解释器与文本编辑器的异同:

1、相同点:python解释器是解释执行文件内容的,因而python解释器具备读py文件的功能,这一点与文本编辑器一样

2、不同点:文本编辑器将文件内容读入内存后,是为了显示或者编辑,根本不去理会python的语法,而python解释器将文件内容读入内存后,可不是为了给你瞅一眼python代码写的啥,而是为了执行python代码、会识别python语法

三、字符编码

重点:什么是字符编码
人类能识别的是字符等高级标识符,电脑只能识别0,1组成的标识符,要完成人与机器之间的信息交流,
一定需要一个媒介,进行两种标识符的转化(两种标识符的对应关系)

对应关系形成的结构称之为:编码表

了解:编码表的发展史

1. ascii(ASCII):字母、数字、英文符号与计算机01标识符的对应关系
思考:如何将128个字符用01完全标签
二进制:11111111 => 255 => 1bytes(1字节) => 8个二进制位

2.中国:研究汉字与计算机01标识符的对应关系:gb2312 => GBK(***) => GB18030
日本:Shift_JIS
棒子:Euc-kr

3. 制造一个可以完成万国字符与计算机01标识符的对应关系的编码表
编码表:unicode表
py2:ascii,没有按万国编码,原因py2先于万国编码而诞生
py3: utf-8, 采用万国编码来解释文本内容

思考:unicode与utf-8是什么关系?

unicode:用2个字节来存储汉字,用2个字节来存储英文字母,占有空间较多,读取效率极高
utf-8:用3-6个字节来存储汉字,用1个字节来存储英文字母,占有空间较少,读取效率低
总结:内存都是按unicode存储数据,硬盘和cpu采用utf-8来存取数据

unicode与utf-8采用的是一张unicode编码表,utf-8是unicode编码表的体现方式,变长存储数据
变长优点:(大量数据都是以英文存在,所以utf-8空间更小)传输速度更快

四、三种字符串

# unicode字符串,默认字符串
s1 = u'abc你好\n不好'
print(s1)
# 字节字符串
s2 = b'abc123\xb7\xb7'
print(s2)
# 原义字符串: 不对字符串内部做任何操作(eg:\n的转化)
s3 = r'abc你好\n不好'
print(s3)

# 编码与解码
s = '123呵呵'
n_b = bytes(s, encoding='utf-8')
print(n_b)

b = b'123\xe5\x91\xb5\xe5\x91\xb5'
n_s = str(b, encoding='GBK')
print(n_s)


# 重点:*****
# 将u字符串编码成b字符串
print(u'你好'.encode('utf-8'))
# 将b字符串解码成u字符串
print(b'\xe4\xbd\xa0\xe5\xa5\xbd'.decode('utf-8'))
二进制字符串:用来传输的字符串
1. 是字符串(文本字符)的二进制体现,(如视频资源可以转化为二进制字符串,不翻转为视频资源,则就是普通字符串)
2. 二进制字符串按字节为最小存储单位存放数据
3. 所有数据转化为二进制字符串都应该用utf-8进行编码吗? 
      -- 只是文本数据
      -- 视频、音频、图像等此类数据也有编码解码过程,采用的编码表不是utf-8,不用关系编码表

五、文件操作

文件:硬盘中一块存储空间(虚拟的文件)
文件操作:根据文件名来操作硬盘的某块存储空间,操作方式 :读read 写write

1、使用文件的三步骤:

# 1、打开文件,得到文件句柄并赋值给一个变量
变量名 = 文件空间
# 文件路径 操作模式(读|写) 编码
f=open('a.txt','r',encoding='utf-8') #默认打开模式就为r
# 2、通过句柄对文件进行操作
data=f.read()
# 3、关闭文件
f.close()

# f=open('a.txt','r')的过程分析
1、由应用程序向操作系统发起系统调用open(...)
2、操作系统打开该文件,并返回一个文件句柄给应用程序
3、应用程序将文件句柄赋值给变量f

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

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

#强调第二点:
f=open(...)是由操作系统打开文件,那么我们一定要为open指定编码为utf-8

2、打开文件的模式

#1. 打开文件的模式有(默认为文本模式):
r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】
w,只写模式【不可读;不存在则创建;存在则清空内容】
a, 之追加写模式【不可读;不存在则创建;存在则只追加内容】

#2. 对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式)
rb 
wb
ab
注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码

#3. 了解部分
"+" 表示可以同时读写某个文件
r+, 读写【可读,可写】
w+,写读【可读,可写】
a+, 写读【可读,可写】

3、操作文件的方法

#掌握
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() #立刻将文件内容从内存刷到硬盘

案例:完成文本文件的复制(边读边写):

r = open('1.txt','r',encoding='utf-8')
w = open('11.txt','w',encoding='utf-8')
for line in r:   # 遍历就是一行一行读文件的流
    w.write(line)
    w.flush()
w.close()
r.close()


#将文件的关闭交给with,当with逻辑结束后,系统会自动释放文件
with open('1.txt','r',encoding='utf-8') as r:
    with open('11.txt','w',encoding='utf-8') as w:
        for line in r:
            w.write(line)
            w.flush()

完成文本文件的复制(先读后写):

with open('source.txt', 'r', encoding='utf-8') as f1, \
    open('target.txt', 'a', encoding='utf-8') as f2:
    # 读取一个字节,如果是行/文件等结束标识,就返回该标识,否则返回None
    print(f1.newlines)

    first_data = f1.read(9)
    f2.write(first_data)
    f2.flush()

    print(f1.newlines)

    second_data = f1.readline()
    f2.write(second_data)
    f2.flush()

    last_data = f1.read()
    f2.write(last_data)
    f2.flush()

    print(f1.newlines)
    data = f1.read()
    print(data)

借助读写,完成追加:

with open('1.txt','rt',encoding='utf-8') as f1:
    data = f1.read()
    data += '666'
with open('1.txt','wt',encoding='utf-8') as f2:
    f2.write(data)

非文本文件采用二进制模式处理

with open('333.mp4', 'rb') as f1:
    with open('666.mp4', 'wb+') as f2:
        for line in f1:
            f2.write(line)

游标:必须在b模式下操作,seek中偏移的是字节
方法:seek(偏移量,偏移位置)
偏移量:移动的字节数,负数是结合模式1,2往前偏移
偏移位置:
    -- 0:从文件开始位置偏移
    -- 1:从当前游标位置开始偏移
    -- 2:从文件末尾位置开始偏移

文件内容:你好1234567890

# 游标读
with open('source.txt', 'rb') as f:
    d1 = f.read(11)
    print(d1)
    # print(d1.decode('utf-8'))  #字节类型涉及到解码

    # 打印当前游标的位置
    print(f.tell())

    # 游标操作 - 从末尾位置开始
    f.seek(-3, 2)
    d2 = f.read()
    print(d2.decode('utf-8'))  # 890

    # # 游标操作 - 从当前位置开始
    # f.seek(-3, 1)
    # d2 = f.read()
    # print(d2.decode('utf-8'))  # 34567890

    # # 游标操作 - 从头开始
    # f.seek(3, 0)
    # d2 = f.read()
    # # print(d2)
    # print(d2.decode('utf-8'))  # 好1234567890

# 游标写:会覆盖书写
with open('source.txt', 'rb+') as f:
    f.seek(11)
    # print(f.read())
    f.write(b'000')

print("-------------------------------------")
# 案例
with open('001.png', 'rb') as f:
    data = f.read()
print(len(data))

# 在大文件中,开头| 1/3 | 2/3 | 末尾 各取10个字节拼接成秒传的信息依据
# 形成秒传规则
tagData = b''
with open('001.png', 'rb') as f:
    # 通过其他途径(sys模块)来获取文件总大小
    data = f.read()
    length = len(data)
    # 开头
    f.seek(0, 0)
    d1 = f.read(10)
    # 1/3
    f.seek(length // 3, 0)
    d2 = f.read(10)
    # 2/3
    f.seek(length // 3 * 2, 0)
    d3 = f.read(10)
    # 末尾
    f.seek(-10, 2)
    d4 = f.read(10)
    tagData = d1 + d2 + d3 + d4
# 秒传依据
print(tagData)

newData = b""
with open('001.png', 'rb') as f:
    data = f.read()
    length = len(data)
    f.seek(0, 0)
    newData += f.read(10)
    f.seek(length // 3, 0)
    newData += f.read(10)
    f.seek(length // 3 * 2, 0)
    newData += f.read(10)
    f.seek(-10, 2)
    newData += f.read(10)

if newData == tagData:
    print('秒传成功')
else:
    print('慢慢传去吧')
# 自身创建文件操作流,可以清晰知道文件操作的模式
# 若文件是从外界获取的
with open('temp.txt','r',encoding='utf-8') as f:
    print(f.readable())   # 文件是否可读
    print(f.writable())   # 文件是否可写
import sys
# 存放当前文件作为脚本执行的参数:['当前文件的绝对路径',手动传入的参数]
# 脚本文件执行:直接用python解释器运行该文件
print(sys.argv)  #['G:/脱产7期/day09/sys传参的用法.py']

# 命令行执行
length = len(sys.argv)
if length > 1:
    print("有指令传入")
    cmd = sys.argv[1]
    if cmd == '1':
        print("新建文件")
        with open('cmd.txt','w',encoding='utf-8') as w:
            pass
    elif cmd == '2':
        print("书写文件")
        with open(r'G:/脱产7期/day09/cmd.txt','w',encoding='utf-8') as f:
            f.write("写入内容")
    else:
        print("输入有误")
else:
    print("请输入指令")