不間斷 Python 挑戰 Day 27 - 檔案、資料夾與路徑

2022/03/02閱讀時間約 13 分鐘
到目前為止,我們都還是在附檔名為.py的Python檔案執行程式碼,當程式需要與外部的檔案互動,例如讀取文字、表格、或是影像來做分析,或是把程式執行的結果儲存下來,就需要能夠存取外部的檔案。例如,在上一節中,當貪食蛇遊戲結束之後,隨著程式停止執行,該次的分數也就被丟棄,若能將分數記錄下來,下次遊戲開始時便能在螢幕上顯示當前的最高分數,也更接近真實的遊戲情境。

檔案

接著一一說明檔案的開啟、讀取、以及寫入的方法。

開啟檔案

為方便範例的展示,我們在主程式所在的目錄建立一個readme.txt,裡面先打上一些文字。
專案資料夾
readme.txt內容
檔案使用open()函數開啟,語法如下:
open(file, mode='r', encoding=None)
  • file:檔案路徑
  • mode:打開文件的模式,預設為'r',意即僅供讀取。可用的模式有:
  - 'r':讀取(預設值)
  - 'w':寫入,已存在的文件會被清除
  - 'x':排他性創建,若文件已存在則失敗
  - 'a':打開文件用於寫入,若文件已存在則在尾端寫入
  - 'b':二進制模式
  - 't':文字模式(預設值)
  - '+':打開進行更新,可讀可寫
  • encoding:用於解碼或編碼文件的格式
檔案使用open()函數所開啟後,存入file物件。
# 讀取檔案
file = open("readme.txt", mode='r', encoding="utf-8")

讀取檔案

檔案開啟後,可使用read()函數讀取該檔案,存成一個字串,之後便可使用print()函數將檔案內容印出。
data = file.read()
print(data)
file.close()
執行結果:
這是python說明檔。
今天將介紹檔案的讀寫、資料夾與路徑。
持續學習!

開啟檔案的另一種方式:with關鍵字

當使用open()函數開啟檔案時,必須在檔案使用完畢後,用close()函數關閉檔案,否則在程式執行的過程中,該檔案會一直被占用,可能造成不可預期的錯誤。好消息是,Python也提供了使用with關鍵字來開啟檔案的方式,在with關鍵字的區塊結束之後自動關閉檔案,來避免因疏忽而沒有讓close()和open()成對出現的錯誤。
with關鍵字的使用方法如下:
with open(檔案) as 檔案物件:
  對檔案的操作
因此,前面的範例可以這樣改寫:
# 使用with讀取檔案
with open("readme.txt", mode='r', encoding="utf-8") as file:
  data = file.read()
  print(data)
也會得到一樣的執行結果:
這是python說明檔。
今天將介紹檔案的讀寫、資料夾與路徑。
持續學習!

逐行讀取

若想要逐行讀取檔案的內容,可以搭配for迴圈來使用。
# 逐行讀取
with open("readme.txt", mode='r', encoding="utf-8") as file:
  for line in file:
    print(line)
執行結果:
這是python說明檔。
今天將介紹檔案的讀寫、資料夾與路徑。
持續學習!
另外,也可以使用readlines()函數,差別在於此函數會將每一行的內容做為串列的元素存入串列。
# 逐行讀取
with open("readme.txt", mode='r', encoding="utf-8") as file:
  data_list = file.readlines()
  print(data_list)
注意到輸出的結果變成串列,其中也包括了換行字元。
['這是python說明檔。\n', '今天將介紹檔案的讀寫、資料夾與路徑。\n', '持續學習!']
若要去除行末的換行符號,可使用rstrip()函數達成。
for line in data_list:
  print(line.rstrip())
執行結果:
這是python說明檔。
今天將介紹檔案的讀寫、資料夾與路徑。
持續學習!

寫入檔案

當程式執行過程有數據要保存下來時,就要透過寫入檔案的方式來將結果存入檔案內,使用的函數是write()。要注意的是,此時open()函數的開啟模式必須為可以寫入的模式,例如mode='w',程式會自動建立要寫入的檔案,若該檔案原本已存在,則檔案的內容會被清空。
以下範例建立一個檔案來寫入文字,可看到執行後,專案資料夾中就多了一個record.txt檔案,開啟該檔案即可看到程式所寫的內容。
# 寫入檔案
with open("record.txt", mode='w', encoding="utf-8") as file:
  file.write("檔案寫入範例\n")
專案資較夾
寫入到record.txt

附加檔案

寫入的內容也可以附加到檔案的末端,此時open()函數的開啟模式必須為mode='a',若開啟的檔案不存在,程式會自動建立要寫入的檔案,若該檔案原本已存在,寫入的內容會附加到檔案的末端。
# 附加檔案
with open("record.txt", mode='a', encoding="utf-8") as file:
  file.write("附加檔案範例\n")

資料夾與路徑

絕對路徑

路徑從根目錄開始,例如今天的範例,main.py在我的電腦的絕對路徑為:/User/wjweng/PyCharmProjects/marathon_python/Day27/main.py
專案資料夾

相對路徑

在main.py的主程式裡,若要使用到其它檔案的內容,除了絕對路徑以外,也可用相對路徑來表達,所謂相對亦即相對於main.py所在的工作目錄的位置,例如main.py的相對路徑即為main.py或./main.py,"."指的是當前的資料夾,在使用上"./"可以省略。
當我們把上一節的貪食蛇遊戲程式加入本節的專案後,若我們要找scoreboard.py,它的相對路徑會變成./snake/scoreboard.py或snake/scoreboard.py。
專案資料夾加入貪食蛇遊戲

取得工作目錄

Python的os模組為處理作業系統相關及目錄路徑的模組,使用os模組的getcwd()方法可以取得當前的工作目錄。
# 取得目前的工作目錄
import os
print(os.getcwd())
執行結果:
C:\Users\wjweng\PycharmProjects\marathon_python\Day27

取得絕對路徑

使用os.path模組的abspath(path)方法可傳回path的絕對路徑。
# 取得絕對路徑
print(os.path.abspath("main.py"))
執行結果:
C:\Users\wjweng\PycharmProjects\marathon_python\Day27\main.py

取得相對路徑

使用os.path模組的relpath(path[, start])方法可傳回path相對於start的路徑,若省略start,則回傳相對於工作目錄的路徑。
# 取得相對路徑
print(os.path.relpath("c:\\"))
執行後所顯示的".."代表上一層資料夾,這裡代表我的c槽位置為工作目錄的上五層。
..\..\..\..\..

檢查路徑

使用os.path模組的exists(path)方法可檢驗path的檔案或資料夾是否存在,若存在則回傳True,反之則為False。
# 檢查路徑
print(os.path.exists("snake/scoreboard.py"))
print(os.path.exists("snake/highest_score.txt"))
從前面專案資料夾的內容可知,snake資料夾內有scoreboard.py,但沒有highest_score.txt,所以分別回傳True跟False。
True
False

貪食蛇最高分記錄補完

有了以上的預備知識後,就可以來為上一節的貪食蛇遊戲加上記錄最高分的功能,新增的部分用粗體字顯示。我們在snake資料夾新增一個highest_score.txt檔案,在每次遊戲結束後用於記錄最高分數,若該次遊戲有破紀錄就更新檔案。在初始化函數中,先檢查highest_score.txt檔案是否存在,如果存在,則將分數顯示在畫布上,反之則將最高分初始化為0。在game_over()方法裡面,判斷該次遊戲的分數是否超過最高分,若是,則將分數寫入highest_score.txt裡面。
# scorebaord.py
class Scoreboard(Turtle):
  def __init__(self):
    super().__init__()
    self.score = 0
    self.hideturtle()
    self.penup()
    self.color(SCORE_COLOR)
    self.speed("fastest")
    self.goto(SCORE_POSITION)
    if path.exists("snake/highest_score.txt"):
      with open("snake/highest_score.txt", mode='r') as score_file:
        self.highest_score = int(score_file.read())
    else:
      self.highest_score = 0
    self.write(f"score: {self.score}, highest_score: {self.highest_score}", False, align="center", font=("Arial", 20, "normal"))
  def get_score(self):
    self.score += 1
    self.clear()
    self.write(f"score: {self.score}, highest_score: {self.highest_score}", False, align="center", font=("Arial", 20, "normal"))
 
  def game_over(self):
    self.color(GAMEOVER_COLOR)
    self.goto(GAMEOVER_POSITION)
    self.write("Game Over", False, align="center", font=("Arial", 40, "normal"))
 
    if self.score > self.highest_score:
      with open("snake/highest_score.txt", mode='w') as score_file:
        score_file.write(str(self.score))
在第一次遊戲結束後,可看到snake資料夾裡面多了一個highest_score.txt,裡面記錄我第一次遊戲的分數。
第一次遊戲結束
第二次進行遊戲即可在上方highest_score的位置看到上一次遊戲(也就是目前的最高分)的分數了。
第二次遊戲

程式範例

為什麼會看到廣告
Wei-Jie Weng
Wei-Jie Weng
留言0
查看全部
發表第一個留言支持創作者!