python基础五(文件操作)

一 文件操作

一 介绍

计算机系统分为:计算机硬件,操作系统,应用程序三部分。

我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,这就涉及到应用程序要操作硬件,众所周知,应用程序是无法直接操作硬件的,这就用到了操作系统。操作系统把复杂的硬件操作封装成简单的接口给用户/应用程序使用,其中文件就是操作系统提供给应用程序来操作硬盘虚拟概念,用户或应用程序通过操作文件,可以将自己的数据永久保存下来。

有了文件的概念,我们无需再去考虑操作硬盘的细节,只需要关注操作文件的流程:

#1. 打开文件,得到文件句柄并赋值给一个变量
#2. 通过句柄对文件进行操作
#3. 关闭文件

 

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

    用户/应用程序(open())
    操作系统(文件)
    计算机硬件(硬盘)
   2、为何要用文件
   用户/应用程序可以通过文件将数据永久保存到硬盘中
   既操作文件就是操作硬盘

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

   3、如何用文件:open()
   控制文件读写内容的模式:t和b
            强调:t和b不能单独使用,必须要跟r/w/a连用

#           t文本(默认的模式)
                1、读写都以str(unicode)为单位的
                2、文本文件
                3、必须指定encoding='utf-8'

#           b二进制/bytes

#       控制文件读写操作的模式
#           r只读模式
#           w只写模式
#           a只追加写模式
#           +:r+、w+、a+

# 没有指定unccoding参数操作系统会使用自己默认的编码
# linux系统默认utf-8
# windows系统默认gbk
with open(r'a.txt', mode='rt', encoding='utf-8') as f:
res = f.read() # t模式会将f.read()读出的结果解码成unicode
print(f, type(f))
print(res)

# 内存:utf-8格式的二进制-----解码(decoding)----》unicode
# 硬盘(a.txt内容:utf-8格式的二进制)

# t模式方便了文本的读写,不然还有个unicode的编码和解码

 

二 在python中

#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.close(),对于这些不长脑子的同学,我们推荐傻瓜式操作方式:使用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)
#强调第二点:
f=open(...)是由操作系统打开文件,那么如果我们没有为open指定编码,那么打开文件的默认编码很明显是操作系统说了算了,操作系统会用自己的默认编码去打开文件,在windows下是gbk,在linux下是utf-8。
这就用到了上节课讲的字符编码的知识:若要保证不乱码,文件以什么方式存的,就要以什么方式打开。

f=open('a.txt','r',encoding='utf-8')

五 python2中的file与open

#首先在python3中操作文件只有一种选择,那就是open()

#而在python2中则有两种方式:file()与open()
两者都能够打开文件,对文件进行操作,也具有相似的用法和参数,但是,这两种文件打开方式有本质的区别,file为文件类,用file()来打开文件,相当于这是在构造文件类,而用open()打开文件,是用python的内建函数来操作,我们一般使用open()打开文件进行操作,而用file当做一个类型,比如type(f) is file

二 打开文件的模式

# coding:gbk
# 1、打开文件
# windows路径分隔符问题
# open('c:\a\liuqiao\d.txt')
# 解决方案一:推荐
# open(r'c:\a\liuqiao\d.txt')
# 解决方案二:
# open('c:/a/liuqiao/d.txt')

f = open(r'E:\Python学习\python全栈学习\day11\a.txt', mode='rt', encoding='utf-8')  # f的值是一种变量,占用的是应用程序的内存空间
print(f)
print(type(f))  # <_io.TextIOWrapper name='E:\\学习\\Python学习\\pycharm编程实践\\day11\\a.txt' mode='r' encoding='cp936'>
# f是文本数据类型
#
# 2、操作文件:读/写文件,应用程序对文件的读写请求都是在向操作系统发送
# 系统调用,然后由操作系统控制硬盘把输入读入内存、或者写入硬盘
res = f.read()
print(res)
# print(f.read())
# 3、关闭文件
# f.close()   #回收操作系统资源
# print(f)  #变量f存在,但是不能再读
# f.read()#变量f存在,但是不能再读

 

件句柄 = open('文件路径', '模式')
模式可以是以下方式以及他们之间的组合:
Character Meaning
‘r' open for reading (default)
‘w' open for writing, truncating the file first
‘a' open for writing, appending to the end of the file if it exists
‘b' binary mode
‘t' text mode (default)
‘+' open a disk file for updating (reading and writing)
‘U' universal newline mode (for backwards compatibility; should not be used in new code)
#1. 打开文件的模式有(默认为文本模式):
r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】
w,只写模式【不可读;不存在则创建;存在则清空内容】
a, 之追加写模式【不可读;不存在则创建;存在则只追加内容】

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

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


x, 只写模式【不可读;不存在则创建,存在则报错】
x+ ,写读【可读,可写】
xb
# 回车与换行的来龙去脉
http://www.cnblogs.com/linhaifeng/articles/8477592.html

# U模式
'U' mode is deprecated and will raise an exception in future versions
of Python.  It has no effect in Python 3.  Use newline to control
universal newlines mode.

# 总结:
在python3中使用默认的newline=None即可,换行符无论何种平台统一用\n即可

三 操作文件的方法

以t模式为基础进行内存操作


# 1、r(默认的操作模式):只读模式,当文件不存在时报错,当文件存在时文件指针跳到开始位置
with open(r'a.txt', mode='rt', encoding='utf-8') as f:
    print('第一次读'.center(30, '*'))
    res = f.read()
    print(res)
    print('第二次读'.center(30, '*'))
    res1 = f.read()
    print(res1)

# 小练习:实现用户认证功能 # 方案一 inp_name = input('请输入你的名字: ').strip() inp_pwd = input('请输入你的密码: ').strip() with open(r'user.txt', mode='r', encoding='utf-8') as f: res = f.read() l1 = res.split() # 以\n为界切分成列表 print(l1) for l2 in l1: # 遍历列表,生成新的name和pwd的列表 u, p = l2.split(':') # 列表解压赋值 print(u, p) if inp_name == u and inp_pwd == p: print('登录成功') break else: print('账号名或者密码错误') # 方案二 inp_name = input('请输入你的名字: ').strip() inp_pwd = input('请输入你的密码: ').strip() with open(r'user.txt', mode='r', encoding='utf-8') as f: for line in f: # 生成的line是字符串(含了.read的功能),先.strip去除字符串前后的\n,再.split以':'为界切分成列表。 print(line, end='') # liuqiao:123\n print(line.strip('\n').split(':')) u, p = line.strip('\n').split(':') # 把用户输入的名字与密码与读出内容做比对 if inp_name == u and inp_pwd == p: print('登录成功') break else: print('账号名或者密码错误') # 应用程序---》文件 # 应用程序---》数据库管理软件----》文件
# 2、w:只写模式 ,当文件不存在时会创建空文件,当文件存在会清空文件,指针位于开始位置
with open('test.txt', mode='wt', encoding='utf-8') as f:
    f.write('x')
    f.write('lq')

# 强调1
# 在以w模式打开文件没有关闭的情况下,连续写入,新的内容总是跟在旧的之后
with open('a.txt', mode='wt', encoding='utf-8') as f:
    f.write('I love you !\n')
    f.write('real\n')
    f.write('yes\n')

# 强调2
# 如果重新以w模式打开文件,则会清空文件内容

# 案例:w模式用来创建全新的文件
# 文本文件的copy工具
with open('a.txt', mode='r', encoding='utf-8') as f1, \
        open('c.txt', mode='w', encoding='utf-8') as f2:
    res = f1.read()
    f2.write(res)
# 路径copy
# 3、a:只追加写,在文件不存在时会创建空文档,文件存在会将文件指针直接移动到文件末尾
with open('aaa.txt', mode='at', encoding='utf-8') as f:
    f.write('I love you !\n')
    f.write('real\n')
    f.write('yes\n')
'''
#强调 w 模式与 a 模式的异同:
# 1 相同点:在打开的文件不关闭的情况下,连续的写入,新写的内容总会跟在前面写的内容之后
# 2 不同点:以 a 模式重新打开文件,不会清空原文件内容,会将文件指针直接移动到文件末尾,新写的内容永远写在最后
'''
# 案例:a模式用来在原有的文件内存的基础之上写入新的内容,比如记录日志、注册功能
# 注册功能
ipname = input('请输入账号:').strip()
ippasword = input('请输入密码:').strip()
with open('register.txt', mode='a', encoding='utf-8') as f:
    f.write('{}:{}\n'.format(ipname, ippasword))
# 4、了解:+不能单独使用,必须配合r、w、a
with open('c.txt', mode='r+', encoding='utf-8') as f:
    f.read()
    f.write('南平\n')
    f.write('南平平\n')

# w+,a+,.read()从指针位置开始读

# r+ w+ a+ :可读可写
# 在平时工作中,我们只单纯使用r/w/a,要么只读,要么只写,一般不用可读可写的模式

以b模式为基础进行内存操作

t:
1、读写都是以字符串(unicode)为单位
2、只能针对文本文件
3、必须指定字符编码,既必须指定encoding参数
b:binary模式
1、读写都是以bytes为单位
2、可以针对所有文件
3、一定不能指定字符编码,一定不要指定encoding参数
with open(r'C:\Users\qiao\Desktop\test1.jpg', mode='rb') as f:
    res = f.read()  # 硬盘的二进制读入内存--->b模式,不做任何转换,直接读入内存
    print(res)  # bytes类型-->当成二进制
    print(type(res))

with open(r'aa.txt', mode='rb') as f:
    res = f.read()  # utf-8的二进制
    print(res)
    print(type(res))
    res1 = res.decode()    # 解码,就是t模式
    print(res1)
强调:b模式对比t模式
1、在操作纯文本文件方面t模式帮我们省去了编码与解码的环节,b模式则需要手动编码与解码,所以此时t模式更为方便
2、针对非文本文件(如图片、视频、音频等)只能使用b模式
# 例子b模式下的w模式,文本举例
with open(r'bb.txt', mode='wb') as f:
    res = f.write('小宝小小宝'.encode('utf-8'))
# 文件拷贝工具
src_file = input('源文件路径: ').strip()
dst_file = input('目标文件路径: ').strip()
with open(r'{}'.format(src_file), mode='rb') as read_f, open(r'{}'.format(dst_file), mode='wb') as write_f:
    for line in read_f:
        print(line)
        write_f.write(line)

循环读取文件:

# 方式一:自己控制每次读取的数据的数据量
with open(r'test1.jpg', mode='rb') as f:
    while True:
        res = f.read(1024)  # 1024个字节为单位读取,数量是编写者为准
        if len(res) == 0:
            break
        print(res)
        print(len(res))
# 方式二:以行为单位读,当一行内容过长时会导致一次性读入内容的数据量过大
with open(r'cc.txt', mode='rb') as f:
    for line in f:
        print(line)

with open(r'test1.jpg', mode='rb') as f1:
    for line in f1:
        print(line)  # bytes类型数据也可通过for循环一行一行的读出来,\n为一行

文件操作的其他方法

# 一、读操作
# 1、readline:一次读一行,读取一行内容,光标移动到第二行首部
with open(r'aa.txt', mode='rt', encoding='utf-8') as f:
    res = f.readline()
    print(res, end='')  # end默认'\n'
    res1 = f.readline()
    print(res1, end='*')

with open(r'aa.txt', mode='rt', encoding='utf-8') as f1:
    while True:
        res2 = f1.readline()
        if len(res2) == 0:
            break
        print(res2, end='')

# 2、readlines:读取每一行内容,存放于列表中
with open(r'aa.txt', mode='rt', encoding='utf-8') as f:
    res = f.readlines()
    print(res)  # end默认'\n'  ['liuqiao\n', '刘巧\n', 'xiaobao']

# f.read()与f.readlines()都是将内容一次性读入内容,如果内容过大会导致内存溢出
# 二:写相关操作
# f.writeline():
with open(r'dd.txt', mode='wt', encoding='utf-8') as f:
    f.writelines(['小宝\n', 'xiaobao\n', '123'])
with open(r'dd.txt', mode='rt', encoding='utf-8') as f1:
    res = f1.read()
    print(res, type(f1.read()))
    print(f1.read())  # print在with子代码下,打印不出得,已关闭文件了
with open(r'ee.txt', mode='wb') as f:
    # 补充1:如果是纯英文字符吗,可以直接加前缀b得到bytes类型
    # l = [b'lq', b'xiaobao', b'123']

    # 补充2:'上'.encode('utf-8')等同于bytes('上',encoding('utf-8')
    l = [bytes('小宝\n', encoding='utf-8'), bytes('xiaobao\n', encoding='utf-8'), b'xiaobao123']
    f.writelines(l)
# flush:立刻将文件内容从内存刷到硬盘,因为操作系统会在内存存了一堆数据后,再写入硬盘,操作系统已优化,flush用来测试
with open(r'ff.txt', mode='wt', encoding='utf-8') as f:
    f.write('小宝123')
    f.flush()
# 三.了解
with open(r'hh.txt', mode='wt', encoding='utf-8') as f:
    print(f.readable())
    print(f.writable())
    print(f.encoding)
    print(f.closed)
print(f.closed)

四、控制文件指针的移动

# 指针移动的单位都是一bytes字节为单位
# 只有有一种情况特殊:
#       t模式下的read(n),n代表的是字符个数

# f.seek(n,模式):n指的是移动的字节个数
# 模式:
# 模式0:参照物是文件开头位置
# f.seek(9,0)
# f.seek(3,0)   # 3

with open(r'ff.txt', mode='rt', encoding='utf-8') as f:
    f.seek(3, 0)  # 参照文件开头移动了3个字节
    print(f.tell())  # 查看当前文件指针距离文件开头的位置,输出结果为3
    print(f.read())  # 从第3个字节的位置读到文件末尾,输出结果为:宝123;read从指针位置开始,往后读

# 注意:由于在t模式下,会将读取的内容自动解码,所以必须保证读取的内容是一个完整中文数据,否则解码失败,一个中文是3个bytes

with open('ff.txt', mode='rb') as f:
    f.seek(6, 0)
    print(f.tell())
    print(f.read().decode('utf-8'))  # 输出结果为:123
# 模式1:参照物是当前指针所在的位置
# f.seek(9,1)
# f.seek(3,1)   #12
with open(r'ff.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:参照物是文件末尾位置,应该倒着移动
# f.seek(9,2) #末尾了,不能在向右移动,还是末尾位置
# f.seek(-9,2)    #应该倒着移动
with open(r'ff.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'))  # 输出结果为:123
自己总结文件内容b模式下:f.write('xxx'.encode('utf-8'));f.read().decode('utf-8'),写是编码,读是解码。
# 之前文件内指针的移动都是由读/写操作而被动触发的,若想读取文件某一特定位置的数据,则则需要用f.seek方法主动控制文件内指针的移动,详细用法如下:
# f.seek(指针移动的字节数,模式控制):
# 模式控制:
# 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
# 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的
# 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的
# 强调:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用

# f.tell()  #获取文件指针当前位置

五、文件修改的两种方式

# 强调:
# 1、硬盘空间是无法修改的,硬盘中数据的更新都是用新内容覆盖旧内容
# 2、内存中的数据是可以修改的

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

# 2 文件修改方式二
# 实现思路:以读的方式打开原文件,以写的方式打开一个临时文件,一行行读取原文件内容,修改完后写入临时文件...,删掉原文件,将临时文件重命名原文件名
# 优点: 不会占用过多的内存
# 缺点: 在文件修改过程中同一份数据存了两份
import os

with open('aaa.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('zzzzdddd', 'xiaobao'))

os.remove('aaa.txt')
os.rename('.db.txt.swap', 'aaa.txt')

 六、作业

#作业二:请闭眼写出购物车程序
#需求:
注册用户名和密码存放于文件中,格式为:egon,123,10000并输入工资收入
启动程序后,先登录,登录成功,然后打印商品列表,失败则重新登录,超过三次则退出程序
允许用户根据商品编号购买商品
用户选择商品后,检测余额是否够,够就直接扣款,不够就提醒
可随时退出,退出时,打印已购买商品和余额
product_list = [['Iphone7', 5800],
                ['Coffee', 30],
                ['疙瘩汤', 10],
                ['Python Book', 99],
                ['Bike', 199],
                ['ViVo X9', 2499],
                ]

shopping_cart = {}
db_file = r'db.txt'
current_user_info = []

while True:
    print('''
1、登录
2、注册
3、购物
    ''')
    choice = input('请输入选择:').strip()
    # 登录
    if choice == '1':
        count = 0
        tag = True
        while tag:
            if count == 3:
                print('输入错误超过三次,请重新登录!')
                break
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            with open(db_file, mode='rt', encoding='utf-8') as f:
                for line in f:
                    line = line.strip('\n')
                    user_info = line.split(',')
                    if username == user_info[0] and password == user_info[1]:
                        print('登录成功')
                        current_user_info = [user_info[0], user_info[2]]
                        print('用户信息:', current_user_info)
                        tag = False
                        break
                else:
                    print('账号或密码错误')
                    count += 1

    # 注册
    if choice == '2':
        reg_username = input('请输入注册用户名:')
        while True:
            reg_password = input('请输入注册用密码:')
            reg_password1 = input('再次输入注册用户密码:')
            if reg_password == reg_password1:
                break
            else:
                print('两次密码不一致,请重新注册!')

        income = input('请输入工资收入:')

        with open(db_file, mode='at', encoding='utf-8') as f:
            f.write('\n{},{},{}'.format(reg_username, reg_password, income))

    # 购物
    if choice == '3':
        if len(current_user_info) == 0:
            print('请先登录!')
        else:
            # 登录成功,请购物
            print('先生{}您好,你的余额还有{}元,请尽情购物!!!'.format(current_user_info[0], current_user_info[1]))
            uname_of_db = current_user_info[0]
            income_db = current_user_info[1]
            tag = True
            while tag:
                for index, product in enumerate(product_list):
                    print(index, product)
                choice = input('请输入商品编号,输入q退出:').strip()
                if choice.isdigit():
                    choice = int(choice)
                    if choice < 0 or choice > len(product_list):
                        continue
                    p_name = product_list[choice][0]
                    p_price = product_list[choice][1]
                    income_db = int(current_user_info[1])
                    if p_price < income_db:
                        if p_name in shopping_cart:  # 以前买过
                            shopping_cart[p_name]['count'] += 1
                        else:
                            shopping_cart[p_name] = {'p_price': p_price, 'count': 1}
                        income_db -= p_price
                        current_user_info[1] = income_db
                        print('增加了商品+{}+到了购物车里'.format(p_name))
                    else:
                        print('穷逼你买不起,商品价格是{}元,你还差{}元!'.format(p_price, p_price - income_db))
                    print(shopping_cart)
                elif choice == 'q':
                    print('''
------------------- 已购买商品列表 -------------------
id       商品           数量        单价       总价
                    ''')
                    total_cost = 0
                    for i, key in enumerate(shopping_cart):
                        print('%22s%18s%18s%18s%18s' % (
                            i,
                            key,
                            shopping_cart[key]['count'],
                            shopping_cart[key]['p_price'],
                            shopping_cart[key]['p_price'] * shopping_cart[key]['count']
                        ))
                        total_cost += shopping_cart[key]['p_price'] * shopping_cart[key]['count']

                    print("""
您的总花费为: %s
您的余额为: %s
---------------------------------end---------------------------------
                                           """ % (total_cost, income_db))
                    while tag:
                        inp = input('确认购买(yes/no?)>>: ').strip()
                        if inp not in ['Y', 'N', 'y', 'n', 'yes', 'no']: continue
                        if inp in ['Y', 'y', 'yes']:
                            # 将余额写入文件
                            import os

                            with open(db_file, mode='rt', encoding='utf-8') as f_read, \
                                    open(r'swp.txt', mode='wt', encoding='utf-8') as f_write:
                                for line in f_read:
                                    if line.startswith(current_user_info[0]):
                                        l = line.strip('\n').split(',')
                                        l[2] = str(income_db)
                                        line = ','.join(l) + '\n'
                                    f_write.write(line)
                            os.remove(db_file)
                            os.rename(r'swp.txt', db_file)
                            print('购买成功')
                            print(current_user_info)
                        shopping_cart = {}
                        current_user_info = []
                        tag = False
                else:
                    print('输入有问题')
    else:
        print('输入非法')

 

posted @ 2023-03-25 12:03  coder雪山  阅读(92)  评论(0编辑  收藏  举报