Python入门之(6.1)文件的操作

目录

文件的操作

1.1 文件的打开与关闭

1.2 读文件

1.3 写文件

1.4 文件的其他操作

1.5 pickle模块


如果要把数据永久保存下来,需要存储在文件中。Python可以处理操作系统下的文件结构,并对文本文件、二进制文件及其他类型的文件,如电子表格文件等进行输入和输出操作。另外,Python还可以管理文件和目录。

文件的操作

到目前为止,程序中所有要输入的数据都是从键盘输入,程序运行结果输出到显示器,所有的输入和输出信息都无法永久保留。Python中的文件机制,使得程序的输入或输出与存储器中的文件相关联。

1.1 文件的打开与关闭

在Python中访问文件,必须首先使用内置方法open()打开文件,创建文件对象,再利用该文件对象执行读写操作。

一旦成功创建文件对象,该对象便会记住文件的当前位置,以便执行读写操作,这个位置称为文件的指针。凡是以r,r+,rb+的读文件方式,或以w,w+,wb+的写文件方式打开的文件,初始时,文件指针均指向文件的头部。

1.内置方法open()

open()方法的语法格式为:   fileObject.open(file_name[,access_mode][,buffering])

功能:打开一个文件并返回文件对象。如果文件不能打开,抛出异常OSError。

参数说明:

  1. file_name变量是要访问的文件名。文件所在路径可以使用绝对路径或相对路径;
  2. access_mode是打开文件的模式,可以是只读、写入、追加等。此参数是可选的,默认文件访问模式为只读(r)。其他打开模式如下表所示;
  3. buffering表示缓冲区的策略选择。若为0,不使用缓冲区,直接读写,仅在二进制模式下有效。若为1,在文本模式下使用行缓冲区方式。若为大于1的整数,表示缓冲区的大小。

如果参数buffering没有给出,使用如下默认策略。

  1. 对于二进制文件,采用固定块内存缓冲区方式,内存块的大小根据系统设备分配的磁盘块来决定;
  2. 对于交互的文本文件(使用isatty()判断为True),采用行缓冲的方式。其他文本文件采用与二进制文件一样的方式。
Python 文件打开模式一览表
模式描述
r以只读方式打开一个已存在的文件
rb以二进制格式打开一个已存在的只读文件
r+打开一个已存在的文件用于读写
rb+以二进制格式打开一个已存在的文件用于读写
w打开文件进行写入。如果文件已存在,将其覆盖;如果文件不存在,创建新文件
wb以二进制格式打开一个文件用于写入。如果文件已存在,将其覆盖;如果文件不存在,创建新文件
w+打开一个文件用于读写。如果文件已存在,将其覆盖;如果文件不存在,创建新文件
wb+以二进制格式打开一个文件用于读写。如果文件已存在,将其覆盖;如果文件不存在,创建新文件
a打开一个文件用于追加。如果文件已存在,文件指针位于文件的结尾,即新内容写到已有内容之后;如果文件不存在,创建新文件进行写入
ab以二进制格式打开一个文件用于追加。如果文件已存在,文件指针位于文件的结尾,即新内容写到已有内容之后;如果文件不存在,创建新文件进行写入
a+打开一个文件用于读写。如果文件已存在,文件指针位于文件的结尾,文件以追加模式打开;如果文件不存在,创建新文件用于读写
ab+以二进制格式打开一个文件用于读写。如果文件已存在,文件指针位于文件的结尾;如果文件不存在,创建新文件用于读写

一个文件被打开后,Python创建一个file对象,通过它得到与该文件相关的各种信息。下表是与文件对象相关的属性:

文件对象相关属性
属性描述
closed如果文件已被关闭,返回True,否则返回False
mode返回被打开文件的访问模式
name返回文件的名称
softspace如果用print输出后,必须跟一个空格符,返回False,否则返回True
encoding返回文件编码
newlines返回文件中用到的换行模式,是一个元组对象

打开一个文件并显示相关属性。示例程序如下:

#使用追加方式打开已存在的文件,使用了绝对路径,目录之间用双斜线分隔
myfile=open('E:\\AAAAA级_\\2018_9_3.docx','a+')
#输出文件对象的相关属性
print('文件名:',myfile.name)
print('是否已关闭:',myfile.closed)
print('访问模式:',myfile.mode)
print('文件编码方式:',myfile.encoding)
print('文件换行方式:',myfile.newlines)

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py python ssss.py 5 2
文件名: E:\AAAAA级_\2018_9_3.docx
是否已关闭: False
访问模式: a+
文件编码方式: cp936
文件换行方式: None

Process finished with exit code 0

 

2.文件关闭方法close()

文件打开并操作完毕,应该关闭文件,以便释放所占用的内存空间,或被别的程序打开并使用。

文件对象的close()方法用来刷新缓冲区里所有还没写入的信息,并关闭该文件,之后便不能再执行写入操作。

当一个文件对象的引用被重新指定给另一个文件时,Python将关闭之前的文件。

close()方法的语法格式为:    fileObject.close()

功能:关闭文件。如果在一个文件关闭后还对其进行操作,将产生ValueError。

1.2 读文件

Python可以读取文本文件或者二进制文件只需要在文件打开模式中指定即可。打开的文件在读取时可以一次性全部读入,也可以逐行读入,或读取指定位置的内容。Python提供了如下方法来读取打开的文件。

1. read()方法

语法格式:    fileObject.read([count])

功能:在打开的文件中读取一个字符串,从文件的起始位置开始读入。注意,Python中的字符串可以是二进制数据,而不仅仅是文本数据。

参数说明:参数count是从已打开文件中要读取的字节数。如果没有传入count,会尝试尽可能多地读取更多的内容,很可能是直到文件的末尾。

read()方法示例,程序如下:

myfile=open('E:\\AAAAA级_\\shi1.txt','r')    #以只读方式打开一个文件
#用文件对象调用read()方法,读取文件的全部内容,并赋值给字符串变量text
text=myfile.read()
print(text)    #输出文本文件中的所有内容
myfile.close()  #关闭文件
print()
#打开另一个文件
myfile=open('E:\\AAAAA级_\\shi2.txt','r+')
#读取文件中的前30个字节,并赋值给字符串变量text
text=myfile.read(30)
print(text)
myfile.close()

运行结果:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py python ssss.py 5 2
对我来说,你还只是一个小男孩,像其他千万个小男孩一样。我不需要你。你也不需要我。对你来说,我只是一只狐狸,像千万只狐狸一样。不过,如果你驯养我,我们就彼此需要了。对我来说,你将是世上独一无二的,对你来说,我也将是世上独一无二的。
我的生活很单调。我捕捉鸡,人捕捉我。所有鸡一模一样,所有人一模一样。因此我有点烦恼。但是,如果你驯养我,我的生活就会像阳光一样明媚。我熟悉一种与众不同的脚步声。其他脚步声使我钻进地下。你的脚步声仿佛音乐一样会召唤我从洞穴里出来。你看,你看到那边的麦田了吗?我不吃面包。对我来说,小麦一无是处。麦田根本吊不起我的胃口。这令人沮丧!你有金黄色的头发。如果你驯养了我,那可是美妙的事!金黄色的小麦会使我想起你,我会喜欢风吹麦浪的声音。

比如,你在下午四点钟来,从三点钟起,我就开始感到高兴。时间越

Process finished with exit code 0

第二个是读取文件中的30个字节,不是一个汉字占两个字节吗?所以为什么不是15个汉字,还是30个呢

2. readline()方法

语法格式:    fileObject.readline([count])

功能:读取文件的一行,包括行结束符。

参数说明:count是一行中要读取的字节数,默认时,读一行。

3. readlines()方法

语法格式:      fileObject.readlines([count])

功能:把文件的每一行作为一个list的一个成员,并返回该list。内部通过循环调用readline()来实现。

参数说明:count表示读取内容的总字节数,即只读文件的一部分。

4. 文件定位方法

1. fileObject.tell()

功能:返回文件操作标记的当前位置,以文件的开始位置为原点。

2. fileObject.next()

功能:返回下一行,并将文件操作标记移到下一行。

3. fileObject.seek(offset[,whence])

功能:将文件操作标记移动到offset的位置。

参数说明:offset一般是相对于文件的开始位置来计算的,通常为正数。如果提供了whence参数,按如下原则计算偏移量:whence为0,表示从头开始计算;whence为1,表示以当前位置为原点进行计算;whence为2,表示以文件末尾为原点进行计算。需要注意,如果文件以a或a+的模式打开,每次写操作时,文件操作标记会自动返回到文件末尾。

文件定位方法与读取方法示例程序:

#以读方式打开文件,文件路径之前的r表示不使用转义
filehandler=open(r'E:\AAAAA级_\poems.txt','r')
print('read()方法:')
print(filehandler.read())    #读取整个文件
print('readline()方法:')
filehandler.seek(0)
print(filehandler.readline())   #返回文件头,读取1行
print('readlines()方法:')
filehandler.seek(0)
print(filehandler.readlines())   #返回文件头,返回所有行的列表
print('逐行显示列表元素')
filehandler.seek(0)
textlist=filehandler.readlines()
for line in textlist:
    line=line.strip('\n')   #去掉换行符
    print(line)
#移位到第33个字符,从第33个字符开始,显示37和字符的内容
print('seek(33) function')
filehandler.seek(33)
print('tell() function',end=' ')
print(filehandler.tell())    #显示当前位置
print(filehandler.read(37))
print('文件的当前读取位置:',end=' ')
print(filehandler.tell())
filehandler.close()

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py python ssss.py 5 2
read()方法:
If I could save time in a bottle 
the first thing that I would like to do 
is to save every day until eternity passes away 
just to spend them with you 
If I could make days last forever 
if words could make wishes come true 
I would save every day like a treasure and then 
again I would spend them with you
readline()方法:
If I could save time in a bottle 

readlines()方法:
['If I could save time in a bottle \n', 'the first thing that I would like to do \n', 'is to save every day until eternity passes away \n', 'just to spend them with you \n', 'If I could make days last forever \n', 'if words could make wishes come true \n', 'I would save every day like a treasure and then \n', 'again I would spend them with you']
逐行显示列表元素
If I could save time in a bottle 
the first thing that I would like to do 
is to save every day until eternity passes away 
just to spend them with you 
If I could make days last forever 
if words could make wishes come true 
I would save every day like a treasure and then 
again I would spend them with you
seek(33) function
tell() function 33

the first thing that I would like to
文件的当前读取位置: 71

Process finished with exit code 0

编写程序,比较两个文件是否相同。如果不同,输出首次不同处的行号和列号。

设计思路:定义两个文件指针,指向要打开的两个文件。分别逐行读取两个文件,并进行比较。在第一次遇到不同的两行时,再逐列比较,最后输出比较结果。

程序如下:

#定义函数main(),完成文件名输入、比较函数调用和结果输出功能
def main():
    #输入文件所在路径和文件名,如d:\temp\t1.txt
    str1=input('请输入文件1所在路径和文件名:')
    str2 = input('请输入文件2所在路径和文件名:')
    file1=open(str1,'r')      #以只读方式打开文件
    file2=open(str2,'r')
    #用readlines()方法把文件内容逐行读入一个列表对象
    lsfile1=file1.readlines()
    lsfile2=file2.readlines()
    file1.close()    #关闭所打开的文件
    file2.close()
    result,row,col=compareFile(lsfile1,lsfile2)  #调用比较函数
    if result==1:
        #函数的第一个返回结果为1,则相等
        print('这两个文件相等')
    else:
        #函数的第一个返回结果为0,则不相等,后两个参数是行、列所在位置
        print('这两个文件在{}行{}列开始不相等'.format(row,col))
#定义文件比较函数,参数是列表对象
def compareFile(file1,file2):
    #计算第一个列表的元素个数,即行数
    len1=len(file1)
    len2=len(file2)
    minlen1=min(len1,len2)         #计算两个列表的最小行数
    for i in range(minlen1):      #用最小行数进行迭代和比较
        print(file1[i])  #输出两个列表的当前行
        print(file2[i])
        #如果这两行不相等,判断是在哪一列不相等
        if file1[i]!=file2[i]:
            #获得这两行的最小列数
            minlen2=min(len(file1[i]),len(file2[i]))
            for j in range(minlen2):    #用最小的列数进行迭代和比较
                if file1[i][j]!=file2[i][j]:
                    return [0,i+1,j+1]
            else:
                #若这两行的列数不同,则也不相等
                if len(file1[i])!=len(file2[i]):
                    return [0,i+1,minlen2+1]
    else:
        #若两个文件的行数不同,则也不相等
        if len(file1)!=len(file2):
            return [0,minlen1+1,1]
        else:
            return [1,0,0]
#运行main()函数
main()

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py python ssss.py 5 2
请输入文件1所在路径和文件名:E:\AAAAA级_\poem1.txt
请输入文件2所在路径和文件名:E:\AAAAA级_\poem2.txt
If I could save time in a bottle 

If I could save time in a bottle 

the first thing that I would like to do 

the first thing that I would like to do 

is to save every day until eternity passes away 

is to save every day until eternity passes away 

just to spend them with you 

just to spend

这两个文件在4行14列开始不相等

Process finished with exit code 0

 

1.3 写文件

Python可以写文本文件或二进制文件,只需要在文件打开模式中指定相关模式即可。打开的文件可以一次性全部写入,也可以把列表中存储的内容写入文件。Python提供了一下方法来对打开的文件执行写操作。

1.write()方法

语法格式: fileObject.write(str)

功能:把str写到文件中。write()并不会在str后加上一个换行符。

参数说明:参数str是一个字符串,是要写入文件的内容。

注意:

  1. 文件写入后,文件的指针向后移动len(str)个字节;
  2. 如果磁道已坏或磁道已满,会发生异常。

2.writelines()方法

语法格式:fileObject.writelines(seq)

功能:把seq的内容全部写入到文件中,并且不会在字符串的结尾添加换行符。

参数说明:seq是一个列表对象。

3.flush()方法

语法格式:fileObject.flush()

功能:把缓冲区的内容写入硬盘。

文件写入方法示例程序:

#文件打开模式为追加模式
myfile=open(r'E:\AAAAA级_\hello.txt','a+')
poem1='江山鸟逾白,山青花欲燃。\n'  #poem1是一个字符串
#poem2是一个列表
poem2=['绿叶青葱傍石摘,\n','孤根不与众花开。\n','酒阑展卷山窗下,\n','习习香从纸上来。\n']
myfile.write(poem1)    #write()方法写入字符串
myfile.writelines(poem2)   #writeline()方法写入列表
myfile.close()
myfile=open(r'E:\AAAAA级_\hello.txt','r')
print(myfile.read())
myfile.close()

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py python ssss.py 5 2
江山鸟逾白,山青花欲燃。
绿叶青葱傍石摘,
孤根不与众花开。
酒阑展卷山窗下,
习习香从纸上来。


Process finished with exit code 0

编写一个Python程序,把一个较大的文件分割成若干较小的文件。例如,将2GB的大文件分割成小文件,以便通过邮箱传递。同时,提供文件合并功能。

设计思路:文件分割是根据用户要求的分割单位对一个大文件进行截取,统一命名并存放到指定路径中,最后返回所分割的块数。文件合并是把分割后的若干个文件根据次序重新组合成原文件。

程序如下:

#文件分割程序
#导入sys和os包
import sys,os
#定义K和M对应的字节数
kilobytes=1024
megabytes=kilobytes*1000
#默认分割文件大小为200MB
chunksize=int(200*megabytes)
'''
定义分割函数,第一个参数是要分割的文件;第二个参数是分割后的文件存放路径;第三个参数是文件分割单位(字节数),默认是200MB,返回值是分块的数目
'''
def split(fromfile,todir,chunksize=chunksize):
    #检测存放分割后文件的路径是否存在
    if not os.path.exists(todir):
        #如果不存在,创建这个文件夹
        os.mkdir(todir)
    else:
        #否则,删除该文件夹内的所有文件
        for fname in os.listdir(todir):
            os.remove(os.path.join(todir,fname))
        #分割编号初始化为0
        partnum=0
        #用二进制格式打开文件
        inputfile=open(fromfile,'rb')
        #由于文件大小不固定,因此使用不限次数的循环结构
        while True:
            #每次读分割字节数大小的块
            chunk=inputfile.read(chunksize)
            #判断文件是否还有内容
            if not chunk:
                #文件全部处理完毕,退出循环
                break
            #块编号加1
            partnum+=1
            #构造块文件名,并与目标文件夹拼接
            #文件名命名规则:前缀是part,后面接4位格式的数字编号
            filename=os.path.join(todir,('part%04d'%partnum))
            #以二进制写模式打开文件
            fileobj=open(filename,'wb')
            #把当前分块写入文件
            fileobj.write(chunk)
            #关闭文件对象
            fileobj.close()
        #返回分块的数目
        return partnum
#如果是本模块自身运行,执行下面的代码
if __name__=='__main__':
    #输入文件分割所需的信息
    fromfile=input('请输入要分割的文件:')
    todir=input('请输入存放分割后文件的文件夹')
    chunksize=int(input('请输入分割大小(以字节为单位)'))
    #获得绝对路径
    absfrom,absto=map(os.path.abspath,[fromfile,todir])
    print('分割文件',absfrom,'到',absto,'单个文件大小为:',chunksize)
    try:
        #调用分割函数
        parts=split(fromfile,todir,chunksize)
    except:
        #文件操作错误处理
        print('分割错误:')
        print(sys.exc_info()[0],sys.exc_info()[1])
    else:
        #显示分割成功信息
        print('分割成功:',parts,'个文件,位于',absto)

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py python ssss.py 5 2
请输入要分割的文件:E:\split\hello.txt
请输入存放分割后文件的文件夹E:\split\shi
请输入分割大小(以字节为单位)18
分割文件 E:\split\hello.txt 到 E:\split\shi 单个文件大小为: 18
分割成功: 4 个文件,位于 E:\split\shi

Process finished with exit code 0

 

这次的程序信息量有点大,好多知识点之前还没遇到过。比如:os.path的一些方法,错误信息在哪里,程序是从哪一句开始的等。

上面的程序第一次运行的时候结果是错的,然后我就debug,debug出来是正确的,然后再运行就是正确的了,感觉有点奇怪,好像之前也遇到过这种情况

#文件合并程序
#导入包sys,os
import sys,os
'''
定义合并函数,第一个参数是分割后的文件所在路径,第二个参数是合并后的文件名,第三个参数是合并后的文件存放的路径,无返回值
'''
def joinfile(fromdir,filename,todir):
    #如果存放文件的目录不存在,则创建
    if not os.path.exists(todir):
        os.mkdir(todir)
    #如果存放分割文件的路径不存在,则报错
    if not os.path.exists(fromdir):
        print('文件夹错误')
    #用写二进制文件方式打开合并文件
    outfile=open(os.path.join(todir,filename),'wb')
    #把分割文件夹中的所有文件存放到列表中
    files=os.listdir(fromdir)
    #将列表中的所有文件名进行排序
    files.sort()
    #遍历列表中的所有文件
    for file in files:
        #拼接文件夹和文件名
        filepath=os.path.join(fromdir,file)
        #以二进制读的方式打开当前列表元素所表示的文件
        infile=open(filepath,'rb')
        #读入文件的所有内容
        data=infile.read()
        #当前文件内容写入合并文件
        outfile.write(data)
        #关闭所读的文件
        infile.close()
    #关闭合并文件
    outfile.close()
#如果是本模块自身运行,执行下面的代码
if __name__=='__main__':
    #输入合并文件所需信息
    fromdir=input('请输入存放分割后的文件所在的路径:')
    filename=input('请输入合并后的文件名:')
    todir=input('请输入存放合并后文件的文件夹:')
    try:
        #调用合并函数
        joinfile(fromdir,filename,todir)
    except:
        #文件操作错误提示
        print('合并文件错误:')
        print(sys.exc_info()[0],sys.exc_info()[1])
    else:
        #输出合并成功信息
        print('合并成功:',filename,'位于',todir)

运行结果为:

#文件合并程序
#导入包sys,os
import sys,os
'''
定义合并函数,第一个参数是分割后的文件所在路径,第二个参数是合并后的文件名,第三个参数是合并后的文件存放的路径,无返回值
'''
def joinfile(fromdir,filename,todir):
    #如果存放文件的目录不存在,则创建
    if not os.path.exists(todir):
        os.mkdir(todir)
    #如果存放分割文件的路径不存在,则报错
    if not os.path.exists(fromdir):
        print('文件夹错误')
    #用写二进制文件方式打开合并文件
    outfile=open(os.path.join(todir,filename),'wb')
    #把分割文件夹中的所有文件存放到列表中
    files=os.listdir(fromdir)
    #将列表中的所有文件名进行排序
    files.sort()
    #遍历列表中的所有文件
    for file in files:
        #拼接文件夹和文件名
        filepath=os.path.join(fromdir,file)
        #以二进制读的方式打开当前列表元素所表示的文件
        infile=open(filepath,'rb')
        #读入文件的所有内容
        data=infile.read()
        #当前文件内容写入合并文件
        outfile.write(data)
        #关闭所读的文件
        infile.close()
    #关闭合并文件
    outfile.close()
#如果是本模块自身运行,执行下面的代码
if __name__=='__main__':
    #输入合并文件所需信息
    fromdir=input('请输入存放分割后的文件所在的路径:')
    filename=input('请输入合并后的文件名:')
    todir=input('请输入存放合并后文件的文件夹:')
    try:
        #调用合并函数
        joinfile(fromdir,filename,todir)
    except:
        #文件操作错误提示
        print('合并文件错误:')
        print(sys.exc_info()[0],sys.exc_info()[1])
    else:
        #输出合并成功信息
        print('合并成功:',filename,'位于',todir)

 

1.4 文件的其他操作

Python的os模块提供了执行文件处理操作的方法,比如重命名和删除文件。要使用这个模块,必须先导入它,然后才可以调用相关的功能。

1.重命名文件

rename()方法语法格式:  os.rename(oldname,newname)

参数说明:oldname是旧文件名,newname是新文件名

2.删除文件

remove()方法语法格式: os.remove(filename)

参数说明:filename是要删除的文件名

3.清空文件

truncate()方法语法格式: fileObject.truncate()

功能:清空文件对象所指文件的内容

1.5 pickle模块

如果希望透明地存储Python对象(如列表、字典、集合等),而不丢失其身份和类型等信息,需要进行对象序列化过程:这是一个将任何复杂的对象转成对象的文本或二进制表示的过程。同样,在使用的时候,必须能够把序列化后的信息恢复成原有的对象。

Python的pickle模块实现了基本的数据序列化和反序列化功能。通过pickle模块的序列化操作,能够将程序中运行的对象信息保存到文件中并永久存储;通过pickle模块的反序列化操作,能够从文件中获得程序所保存的对象。

使用pickle模块,需要采用二进制方式读写文件。

1.dump()方法

语法格式:    pickle.dump(obj,file[,protocol])

功能:将对象obj保存到文件file中

参数说明:

  1. obj:要保存的对象
  2. file:对象保存到的类文件对象,要求具有write()接口。file可以是一个以‘w’方式打开的文件,或者一个StringIO对象,或者其他任何实现write()接口的对象
  3. protocol:序列化使用的协议版本。若为0,ASCII协议,所序列化的对象使用可打印的ASCII码表示;若为1,老式的二进制协议;若为2,2.3版本引入的新二进制协议,较以前的更高效。其中,协议0和协议1兼容老版本的Python。protocol的默认值为0

2.load()方法

语法格式:pickle.load(file)

功能:从file中读取一个字符串,并将它重构为原来的Python对象

参数说明:file是类文件对象,要求具有read()和readline()接口

编写一个Python程序,模拟一个四则运算练习。学生每次完成10个题目的练习并给出成绩,可多次练习。要求最近3次练习的题目不能重复,记录所有练习的成绩,并将相关信息保存在文件中。

设计思路:使用列表来保存最近3次练习的题目和答案。首先,每次练习随机生成10个不重复的题目,同时生成答案;然后,用户练习最新生成的题目,并输出成绩和答案;最后,输出所有历史成绩。

程序如下:

import random    #导入随机数包
import pickle    #导入pickle模块
import os        #导入os包
def practice():   #定义练习系统函数
    formular=setQuestion()         #调用题目生成函数,获得最新题目列表
    answerQuestion(formular)
#定义题目生成函数,返回值是最新题目列表
def setQuestion():
    count=0    #count用于计算出题的数目
    opr={1:'+',2:'-',3:'*',4:'/'}   #定义编号与运算符关系字典
    formular=[]          #题目列表初始化
    #判断存放旧题的文件是否存在
    if os.path.exists(r'E:\AAAAA级_\question.dat'):
        #如果旧题文件存在,则打开该文件
        pkFile=open(r'E:\AAAAA级_\question.dat','rb')
        #把文件中的列表元素赋值给列表对象
        data1=pickle.load(pkFile)
        pkFile.close()
    else:
        data1=[]    #否则,对象列表置空
    while True:    #不限次数循环结构,用于产生不同的题目
        #分别获得两个操作数和运算符
        num1=random.randint(1,100)
        num2=random.randint(1,100)
        opnum=random.randint(1,4)
        op=opr.get(opnum)   #在字典中查找编号所对应的运算符
        #计算当前算式的运算结果,其中,除法只取商
        result={
            '+':lambda x,y:x+y,
            '-':lambda x,y:x-y,
            '*':lambda x,y:x*y,
            '/':lambda x,y:int(x/y)
        }[op](num1,num2)
        #生成一个元组,作为列表中的一个对象
        tmp=(num1,op,num2,result)
        if tmp in formular or tmp in data1:
            #若题目重复,则重新出题
            continue
        formular.append(tmp)   #不重复,则把题目添加到列表中
        count+=1    #题目数加1
        if count==10:
            break   #题目够10个,则退出循环
    data2= data1[-21:-1]  #从旧题列表中取倒数20个题目
    data2.extend(formular)  #增加新题目
    #以写二进制方式打开文件
    pkFile=open(r'E:\AAAAA级_\question.dat','wb')
    pickle.dump(data2,pkFile,-1)  #把列表保存在文件中
    pkFile.close()
    return formular
#定义用户练习函数,参数为题目列表
def answerQuestion(formular):
    grade=0                           #成绩初始化为0
    for item in formular:
        #输出题目
        print('{}{}{}='.format(item[0],item[1],item[2]),end=' ')
        #用户输入答案
        ans=int(input())
        if ans==item[3]:
            #答案正确,加10分
            grade+=10
    #如果有历史成绩文件,则打开
    if os.path.exists(r'E:\AAAAA级_\grade.dat'):
        pkFile=open(r'E:\AAAAA级_\grade.dat','rb')
        allgrade=pickle.load(pkFile)
        pkFile.close()
    else:
        allgrade=[]
    print('本次成绩为:{}'.format(grade))  #输出本次成绩
    if allgrade:
        print('历史成绩为:{}'.format(allgrade))
    #保存本次成绩到历史成绩文件中
    allgrade.append(grade)
    pkFile=open(r'E:\AAAAA级_\grade.dat','wb')
    pickle.dump(allgrade,pkFile,-1)
    pkFile.close()
    print('本次练习答案为:')
    #输出练习答案
    for item in formular:
        print('{}{}{}={}'.format(item[0],item[1],item[2],item[3]))
if __name__=='__main__':
        #调用练习系统函数
    practice()

 

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py python ssss.py 5 2
39/94= 0
46/15= 3
36-8= 28
15/73= 0
48-80= -32
1/29= 0
26+52= 78
63-100= -37
33-71= -38
31*64= 1984
本次成绩为:100
历史成绩为:[100]
本次练习答案为:
39/94=0
46/15=3
36-8=28
15/73=0
48-80=-32
1/29=0
26+52=78
63-100=-37
33-71=-38
31*64=1984

Process finished with exit code 0

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值