当前位置: 代码迷 >> 综合 >> py实现外星人入侵(二次开发)——1.读入最高分(包含文件的基本操作和文件相对路径讲解)
  详细解决方案

py实现外星人入侵(二次开发)——1.读入最高分(包含文件的基本操作和文件相对路径讲解)

热度:10   发布时间:2023-12-12 23:49:42.0

前言

在之前的博客中,我详细的介绍了一下大战外星人的现目组成,从这篇博客开始,我将尝试用不同的方式来二次开发(魔改)这个项目。
这次我将魔爪放在了最高分部分上,我们知道之前的最高分在每一次重新运行游戏就消失了,这次我们采取将最高分读入到文件中

先写入

写入部分还是比较简单的,这里我采取的思路是在三条命都没了之后、在游戏退出之前写入当前最高分。
虽然书上的意思是在sys.exit()之前,但是玩一半退出了,不给你保留也没什么问题。

首先找到要修改的位置:game_function文件的ship_hit函数
在函数中,我们有判断三条命是否用完了的情况,并对两种结果都进行了操作,我们将在else中添加内容。

然后要考虑,可能之前已经有了这样一个最高分文件,但是对我没有影响,我的想法是简单粗暴一些,反正之前的最高分也不要了,与其取出然后再写入不如直接使用"w"方法直接覆盖之前的文档。

(下面可能有一点啰嗦,简单说了一下几种文件打开类型和打开函数,不感兴趣的直接看代码部分吧)

对了,先介绍一下py的几种常见的读入文件格式:

字母 模式
w 写入模式
r 读取模式
r+ 读写模式
a 附加模式

我们假设之前有一个文件,那么a就是在之前的基础上接着写;
w就是覆盖掉之前的文件,然后重写;
r则是只能读取,不能写入;
r+则比较厉害,能写入和读取。

如果是之前没有文件,w和r+会生成一个空文档;
a也会生成,但是会有一个io.UnsupportedOperation: not readable的报错;
至于r,还是别难为它吧,直接报错文件找不到。

回归正题,别忘了我们采取的是"w"模式。
一般来说打开文件我比较喜欢的是with open,这里也简单说一下吧。
with open的结构是这样的:

with open("file name","模式") as file_object:内容

首先with open是单独的一个模块,主意好缩进;
两个参数分别为文件名和打开类型(不写默认为r);
file_object属于打开文件的别名,学过C的都知道也有类似的东西(不过孩子已经ptsd了,立下flag以后不被逼就不写C)

除此之外,还有:

file_object = open("file name","模式")

不过这是我们就需要使用close函数了,with open相当于是将关闭文件的时间交给py来判断。

代码部分:
#stats.max_name存储了我的最高分文件名称,这个就是自己随便起
with open("./alien/"+stats.max_name,"w") as max_num:highest = str(stats.high_score)max_num.write(highest)       

write方法是将字符串写入文件中,所以我在写入之前将stats中的最高分拿出来做了一下类型转换。

最终是这样的:
在这里插入图片描述

可能会奇怪我这个文件路径吧,这是我文件的组成。
在这里插入图片描述
首先,alien文件夹中包括了我所有的项目文件,我也将最高分文档存储在了这里。
但如果我直接使用state.max_name作为地址则会将文件创建到alien文件夹同一层。

这就要谈到文件地址和执行地址了。
虽然我们的game_functiong.py(也就是我们添加内容的文件)在alien文件夹内,按照这样的说法我们在使用同一文件夹下的max.txt应当只需要stats.max_name,但是很明显这样错了,原因是虽然我们的py文件在文件夹内,但是执行地址是在alien文件夹,所以实际上是在alien文件夹的同一层创建了这样一个txt文件。

因此我们需要使用"./alien/"+stats.max_name的方式来实现先进入alien文件夹,然后创建、打开文档

注意一下,我在vscode中打开的文件夹是ALIEN/alien,alien中存在项目文件和max.txt,
但如果换成直接打开alien文件夹,路径不管怎样都是有问题的。
路径这一难题我会持续关注和修改的,至少这个解释回答了我目前的疑问。

再读取

读取的部分我准备添加在游戏的主程序最开始部分,在游戏循环之前。

首先要解决的一个问题,就是在我第一次进行游戏的时候是没有文档的,所以我需要创建一个文档,但是又不好判断,所以最开始的方式选择的是异常,然后感觉啰嗦,就上网上查了一下,找到os库有现成的函数能判断文件存在与否。
但是我大意了,没有闪,"a+"就能解决我的问题。
(还在纠结为什么没有这样一个方便的模式,原来是学的不深忘记了)

在第三部分我也会讲一下剩下的两个方式,不过这部分我们还是使用"a+"来实现。

    with open("./alien/"+stats.max_name,"a+") as max_num:max_num.seek(0)                     #将文档光标移动到最开始high = max_num.read()high = high.strip()#print("hello"+high+"world") #测试用的if high != "":max_num.write("0")else:stats.high_score = int(high)

对应着write方法,我们也有着读取的read,只不过读取的为字符串,还会没事在最后面给你加一个换行符,还是很烦人的。
我采取的方式是先读取内容,使用strip方法去掉两端的空格、回车,如果最终是空,说明文件没有内容,否则将内容拿出来,转成int型赋值给stats.high_score。
strip是去除两边的空白字符,rstrip是右边,左边是lstrip,为了省事就直接两边一起清。

最后一个问题就是应该放在哪个准确位置,这里我们要知道在创建记分牌对象"sb"的时候,就已经将当前的stats数据拿出来进行处理了,就等着屏幕刷新渲染到窗口上,而且我们在stats中初始化类有一个将high_score赋值为0的操作:
在这里插入图片描述
所以我们只好将其放在stats对象和sb对象中间:
在这里插入图片描述

解决文件问题的其他办法

异常
    try:with open("./alien/"+stats.max_name,"r") as max_num:high = max_num.read()high = high.strip()#print(high)stats.high_score = int(high)except FileNotFoundError:#with open函数的另外一种调用方法,懂的都懂with open("./alien/"+stats.max_name, mode='w', encoding='utf-8') as max_num:max_num.write("0")

因为我们有时候也不知道用户会发生什么,所以需要对一些错误进行判断,才有了这样的一个结构。
说的可能有一点绕,就比如让你输入一个数字进行计算,你敲一个"q",如果没有数字检测就gg了,
但是我们可以使用这样的结构,即使是发生了报错,也能使用另一种输出方式,比如弹出一个"你的输入有问题",也不至于因为不能运行导致程序挂了或者让用户看到报错。

比如:

try:……(在操作中发生了错误)
except 报错内容:错误提示或者补救措施

在我们的程序中,就是先看看能不能打开存储文档,如果打不开会有一个"FileNotFoundError"的报错,这样我们就可以使用"w"方法创建一个文档,并存储上字符串0
**with一定要后面跟内容,不然会"IndentationError: expected an indented block"。**所以我才在最后添加了一个写入0操作。

如果没有报错,像我们这个就是直接在try中实现操作,还可以在expect下面加一个相同缩进的else

try:......
except error:......
else:
import os
#最前面有一个import osif os.path.exists('./alien/'+stats.max_name):with open("./alien/"+stats.max_name,"r") as max_num:high = max_num.read()high = high.strip()stats.high_score = int(high)else:with open("./alien/"+stats.max_name, mode='w', encoding='utf-8') as max_num:max_num.write("0")

简单来说就是使用os自带的方法来进行检查,判断是否存在该文件

总结

好了,这期的魔改就到此结束了,接下来我还会对其他部分进行修改。

  相关解决方案