今日内容:
文件打开模式

读写文件的方法

文件处理高级
    控制文件内指针的移动
    改文件的两种方式

**四 操作文件的方法**
**4.1 重点**
# 读操作
f.read()  # 读取所有内容,执行完该操作后,文件指针会移动到文件末尾
f.readline()  # 读取一行内容,光标移动到第二行首部
f.readlines()  # 读取每一行内容,存放于列表中

**# 强调:**
# f.read()与f.readlines()都是将内容一次性读入内容,如果内容过大会导致内存溢出,
若还想将内容全读入内存,则必须分多次读入,有两种实现方式:
**# 方式一**
with open('a.txt',mode='rt',encoding='utf-8') as f:
    for line in f:
        print(line) # 同一时刻只读入一行内容到内存中

**# 方式二**
with open('1.mp4',mode='rb') as f:
    while True:
        data=f.read(1024) # 同一时刻只读入1024个Bytes到内存中
        if len(data) == 0:
            break
        print(data)

**# 写操作**

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
**文件打开模式**

创建一个a.txt文件

with open(r'a.txt',mode='wt',encoding='utf-8') as f:
    f.write("你好")

with open(r'a.txt',mode='ab') as f:
    f.write("哈哈哈".encode('utf-8'))

with open(r'a.txt',mode='rb') as f:
    data = f.read()
    print(data.decode('utf-8'))

with open(r'1.mp4',mode='rb') as f:
    # data = f.read()
    # print(data)

    for line in f:
        print(line)

with open('1.mp4',mode='rb') as f1,open(r'D:\111111111111.mp4',mode='wb') as f2:
    # data = f1.read()
    # f2.write(data)
    for line in f1:
        f2.write(line)
**可读可写模式**
with open('a.txt',mode='r+t',encoding='utf-8') as f:
    # f.write('次卧')
    f.write('h')
**修改文件的两种方式**

**# 思路一:**
# with open('b.txt',mode='rt',encoding='utf-8') as f1:
#     data = f1.read()
#     res = data.replace('egon','EGON')
#
# with open('b.txt',mode='wt',encoding='utf-8') as f2:
#     f2.write(res)

**# 思路二**
import os

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

os.remove('b.txt')
os.rename('.b.txt.swp','b.txt')
**6.1 文件修改方式一**
# 实现思路:将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件
# 优点: 在文件修改过程中同一份数据只有一份
# 缺点: 会过多地占用内存
with open('db.txt',mode='rt',encoding='utf-8') as f:
    data=f.read()

with open('db.txt',mode='wt',encoding='utf-8') as f:
    f.write(data.replace('kevin','SB'))
**6.1 文件修改方式二**
# 实现思路:以读的方式打开原文件,以写的方式打开一个临时文件,一行行读取原文件内容,修改完后写入临时文件...,删掉原文件,将临时文件重命名原文件名
# 优点: 不会占用过多的内存
# 缺点: 在文件修改过程中同一份数据存了两份
import os

with open('db.txt',mode='rt',encoding='utf-8') as read_f,\
        open('.db.txt.swap',mode='wt',encoding='utf-8') as wrife_f:
    for line in read_f:
        wrife_f.write(line.replace('SB','kevin'))

os.remove('db.txt')
os.rename('.db.txt.swap','db.txt')
**练习**

**注册**

inp_user = input("请输入您的用名:").strip()
inp_pwd = input("请输入您的密码:").strip()
inp_money = input("请输入您的余额:").strip()

with open('db.txt', mode='at', encoding='utf-8') as f:
   f.write('%s:%s:%s\n' % (inp_user, inp_pwd, inp_money))

**转账**
import os

with open('db.txt',mode='rt',encoding='utf-8') as f1,open('.db.txt.swp',mode='wt',encoding='utf-8') as f2:
    for line in f1:
        user,pwd,money = line.strip('\n').split(':')
        if user == 'tom':
            money = int(money) - 100
        elif user == 'lili':
            money = int(money) + 100

        line = "%s:%s:%s\n" % (user, pwd, money)
        f2.write(line)

os.remove('db.txt')
os.rename('.db.txt.swp','db.txt')
**文件读写的其他方法**

with open('b.txt',mode='rt',encoding='utf-8') as f:
    print(f.readable())
    print(f.readline())
    print(f.readline())

    lines = f.readlines()
    print(lines[0])

with open('b.txt', mode='wt', encoding='utf-8') as f:
    print(f.writable())

    lines = ['你好\n', '我好\n', '大家好\n']

    for line in lines:
        f.write(line)
    f.writelines(lines)

    f.write('1111\n222\n333\n')

    f.write('hello')
    f.writelines('hello')

    f.flush()

with open('b.txt', mode='at', encoding='utf-8') as f:
    f.truncate(3)
**储备知识:**

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

with open('b.txt',mode='rt',encoding='utf-8') as f:
    print(f.read(4))

with open('b.txt',mode='rb') as f:
    print(f.read(3).decode('utf-8'))

f.seek(移动的字节个数,模式)
模式:
**0:**参照文件开头,只有0模式可以在t下使用,其他的都只能在b下使用
with open('b.txt',mode='rt',encoding='utf-8') as f:
    f.seek(3,0)
    f.seek(6,0)
    print(f.tell())

**1:**参照指针当前所在的位置,只能在b下使用
with open('b.txt',mode='rb') as f:
    f.seek(3,1)
    f.seek(6,1)
    print(f.tell())

**2:**参照指针文件末尾,通常是倒着移动,只能在b下使用
with open('b.txt',mode='rb') as f:
    f.seek(-5,2)
    f.seek(-8,2)
    print(f.tell())
    print(f.read().decode('utf-8'))
**0模式详解**
# a.txt用utf-8编码,内容如下(abc各占1个字节,中文“你好”各占3个字节)
abc你好

**# 0模式的使用**
with open('a.txt',mode='rt',encoding='utf-8') as f:
    f.seek(3,0)     # 参照文件开头移动了3个字节
    print(f.tell()) # 查看当前文件指针距离文件开头的位置,输出结果为3
    print(f.read()) # 从第3个字节的位置读到文件末尾,输出结果为:你好
    # 注意:由于在t模式下,会将读取的内容自动解码,所以必须保证读取的内容是一个完整中文数据,否则解码失败

with open('a.txt',mode='rb') as f:
    f.seek(6,0)
    print(f.read().decode('utf-8')) #输出结果为: 好

**# 1模式的使用**
with open('a.txt',mode='rb') as f:
    f.seek(3,1) # 从当前位置往后移动3个字节,而此时的当前位置就是文件开头
    print(f.tell()) # 输出结果为:3
    f.seek(4,1)     # 从当前位置往后移动4个字节,而此时的当前位置为3
    print(f.tell()) # 输出结果为:7

**2模式详解**
# a.txt用utf-8编码,内容如下(abc各占1个字节,中文“你好”各占3个字节)
abc你好

**# 2模式的使用**
with open('a.txt',mode='rb') as f:
    f.seek(0,2)     # 参照文件末尾移动0个字节, 即直接跳到文件末尾
    print(f.tell()) # 输出结果为:9
    f.seek(-3,2)     # 参照文件末尾往前移动了3个字节
    print(f.read().decode('utf-8')) # 输出结果为:好

**# 小练习**:实现动态查看最新一条日志的效果
import time
with open('access.log',mode='rb') as f:
    f.seek(0,2)
    while True:
        line=f.readline()
        if len(line) == 0:
            # 没有内容
            time.sleep(0.5)
        else:
            print(line.decode('utf-8'),end='')

**主动控制文件内指针移动**
#大前提:文件内指针的移动都是Bytes为单位的,唯一例外的是t模式下的read(n),n以字符为单位
with open('a.txt',mode='rt',encoding='utf-8') as f:
     data=f.read(3) # 读取3个字符

with open('a.txt',mode='rb') as f:
     data=f.read(3) # 读取3个Bytes

# 之前文件内指针的移动都是由读/写操作而被动触发的,若想读取文件某一特定位置的数据,则则需要用f.seek方法主动控制文件内指针的移动,详细用法如下:
# f.seek(指针移动的字节数,模式控制): 
# 模式控制:
# 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
# 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的
# 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的
# 强调:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用

其他

**转账功能**

创建一个db.txt文件,存入一些账号密码余额
egon:123:1000
lili:456:1000
tom:789:1000
tony:999:1000

import os

with open('db.txt', mode='rt', encoding='utf-8')as f1,open('.db.txt',mode='wt',encoding='utf-8')as f2:
    for line in f1:
        user, pwd, money = line.strip('\n').split(':')
        if user == 'tom':
            new_money = int(money) - 100
            new_line = '%s:%s:%s\n' %(user,pwd,new_money)
            f2.write(new_line)
        elif user == 'lili':
            new_money = int(money) + 100
            new_line = '%s:%s:%s\n' %(user,pwd,new_money)
            f2.write(new_line)
        else:
            f2.write(line)
os.remove('db.txt')
os.rename('.db.txt','db.txt')

**操作一次输出结果     lili:456:1100    tom:789:900    实现ton转账给lili (100元)**  
**优化转账功能代码(删除重复操作的代码)**

import os

with open('db.txt', mode='rt', encoding='utf-8')as f1,open('.db.txt',mode='wt',encoding='utf-8')as f2:
    for line in f1:
        user, pwd, money = line.strip('\n').split(':')
        if user == 'tom':
            money = int(money) - 100
        elif user == 'lili':
            money = int(money) + 100
        line = '%s:%s:%s\n' %(user,pwd,money)
        f2.write(line)
os.remove('db.txt')
os.rename('.db.txt','db.txt')