
由於目前工作之餘,正使用 Python 寫 Side-Project,在過程中,針對檔案與目錄相關的寫法總是反覆查了又查,於是想說抽空寫個相關的 Cheat Sheet 好讓自己快速查閱,但又覺得可能網友們也會遇到同樣的問題,倒不如整理完整一點再分享出來,若是有錯誤也歡迎各位指教,該篇是針對 Python 3 檢查檔案及目錄是否存在 作筆記。
目錄
前言
站長平常幾乎沒再寫程式的,除了偶爾爬爬蟲,但該次的主題是從寫 Python 一直以來都有遇到的問題,常常在處理檔案的時候被路徑搞瘋,「工作目錄」與「執行路」基本上是分開的,工作目錄會依照使用者所在目錄而有不同,而執行目錄則是取決於檔案的位置,該篇文章也針對 Python 3 檢查檔案及目錄是否存在 準備了一個簡單的 Lab,是說很久沒寫 Python 相關的文章了,除了沒下文的 跌 入 數 據 分 析 的 坑 系列以外還有一篇談 C l o u d F i r e s t o r e 新增查刪,實作該篇文章大致上可以學習到以下技能。
- 檢查檔案是否存在
- 檢查目錄是否存在
- 檔案存在,自動更名並建立(隨機、序列)
- 檔案不存在,自動建立
- 目錄不存在,自動建立
- 多層目錄建立
補充
自 Python 3.5 以上的版本新增 pathlib 函式庫,可以輕鬆解決檔案重複、存不存在的問題,該文章以實驗精神土炮的方式實作,有興趣參考之。
測試環境建置 (Linux with Python3)
環境使用 Python3.6 與 Windows 的 WLS 下的 Ubuntu,建置 Lab 環境,只要在 Linux 環境底下,複製以下內容並貼上並戳一下 Enter 即可。
1 2 3 4 5 6 | cd ~/ mkdir demo && cd demo mkdir dir1 && mkdir dir2 touch dir1 /file1 .txt echo "import os\nbase_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)))\nPrint(f'User_Path: {os.getcwd()}')\nprint(f'File_path: {base_dir}')" > dir2 /demo .py python3 |
取得當前目錄
使用者所在目錄
1 2 3 | import os os.getcwd() # /home/{username}/dome |
程式所在目錄
在環境建置的時候有透過指令在 dir2
底下建置一個由 Python3 撰寫的腳本,其程式碼如下,如果有複製貼上環境建置的 bash 腳本,檔案應該是已經建好的狀態。
1 2 3 4 5 6 7 | # /home/mksyi/demo/dir2/demo.py import os base_dir = os.path.join(os.path.dirname(os.path.realpath(__file__))) print (f 'User_Path: {os.getcwd()}' ) print (f 'File_Path: {base_dir}' ) |
由於使用檔案腳本,若是在 Python Shell 底下,必須先使用 quit()
退出 Python Shell,並且在 OS Shell 下執行才行,以下是執行過程與結果。
1 2 3 4 5 6 | ➜ pwd [1:32:10] /home/mksyi/demo ➜ python3 dir2 /demo .py [1:32:12] User_path: /home/mksyi/demo File_path: /home/mksyi/demo/dir2 |
目錄切換
1 2 3 4 | import os os.chdir( "dir1" ) os.getcwd() # /home/{username}/demo/dir1 |
檔案判斷
1 2 3 | import os os.path.isfile( "file2.txt" ) # False |
不存在自動創建檔案
1 2 3 4 5 6 7 8 9 | import os filename = "file2.txt" os.path.isfile(filename) # False if not os.path.isfile(filename): f = open (filename, "a" ) f.write( "Hello World\n" ) f.close() os.path.isfile(filename) # True |
對於 open
的第二個代表的是模式,可以參考下方補充資訊。
若是要創建空檔案,只要把 write()
拿掉即可,如下方範例。
1 2 3 4 5 6 7 8 | import os filename = "file3.txt" os.path.isfile(filename) # False if not os.path.isfile(filename): f = open (filename, "a" ) f.close() os.path.isfile(filename) # True |
若檔案存在,自動更名並創建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import os import random, string def get_newname(o_filename): """ filename file3.txt => file3 extension file3.txt => .txt new_filename file3-[a-z]{5}.txt """ size = 5 filename = os.path.splitext(o_filename)[ 0 ] extension = os.path.splitext(o_filename)[ 1 ] random_string = ''.join(random.choice(string.ascii_letters) for x in range ( 5 )) new_filename = f '{filename}-{random_string}{extension}' return new_filename filename = "file3.txt" if os.path.isfile(filename): filename = get_newname(filename) f = open (filename, "ab" ) f.close() |
該隨機方法參考 [4],當然隨機是一個做法,檔案命名的方式可以依照需求自訂,若以寫 Log 的形式,可以參照每天日期創建新增檔案,又或者可以採用流水號的方式,當 file1 存在嘗試建立 file2 file3 … fileN,缺點是當檔案數量龐大時,檢查的機制會消耗掉些許效能。
目錄判斷
1 2 | import os os.path.isdir( "dir3" ) # False |
目錄不存在自動創建
1 2 3 4 5 6 | import os >>> os.getcwd() # /home/{username}/demo dirname = "dir3" if not os.path.isdir(dirname) os.mkdir(dirname) |
創建多層目錄
創建目錄常遇到的問題 「目錄已經存在」 、「找不到檔案或目錄」。
1 2 3 4 5 6 7 8 9 10 11 | import os >>> os.getcwd() # /home/{username}/demo >>> os.mkdir( 'dir3' ) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> FileExistsError: [Errno 17 ] File exists: 'dir3' >>> os.mkdir( 'dir3/dirA/dirA1' ) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> FileNotFoundError: [Errno 2 ] No such file or directory: 'dir3/dirA/dirA1' |
可以使用 os.makedirs()
來一次創建多層目錄。
1 2 3 4 5 | import os os.makedirs( 'dir3/dirA/dirA1' ) os.chdir( "dir3/dirA/dirA1" ) os.getcwd() # /home/{username}/demo/dir3/dirA/dirA1 |
但仍然要注意,不論是使用 mkdir()
還是 makedirs()
方法都會產生 FileNotFoundError
錯誤訊息,最好使用之前多一個判斷檢查是否已經存在。
1 2 3 4 5 6 7 8 | import os os.getcwd() # /home/{username}/demo dirname = "dir4/dirA/dirA1" os.path.isdir(dirname) # False if not os.path.isdir(dirname): os.makedirs(dirname) os.path.isdir(dirname) # True |
常見例子
很多時候是依照檔案的位置建立檔案或目錄,舉個簡單的例子,有一隻爬蟲程式放在 /home/{username}/demo/crawler/
底下,並且該爬蟲程式會將爬下的資料來放在 demo/crawler/Download
資料夾下,若是使用者在 /home/{username}/
路徑下執行該爬蟲程式,則目錄會被創建在 /home/{username}/Download
,這並不是我們要的,所以要解決該問題,會用到上面的例子提到的 「取得程式所在目錄」 功能,並且針對重複的檔案建立流水號。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | # /home/{username}/demo/crawler/crawler.py import os def get_dirpath(dir_path): dir_path = os.path.join( os.path.dirname( os.path.realpath(__file__)), dir_path ) if not os.path.isdir(dir_path): os.makedirs(dir_path) return dir_path def get_newname(o_filename, sn = 1 ): """ filename /home/{username}/demo/crawler/download/mks_data.txt => /home/{username}/demo/crawler/download/mks_data extension /home/{username}/demo/crawler/download/mks_data.txt => .txt """ if os.path.isfile(o_filename): filename = os.path.splitext(o_filename)[ 0 ] extension = os.path.splitext(o_filename)[ 1 ] new_filename = f '{filename}-{str(sn)}{extension}' if os.path.isfile(new_filename): new_filename = get_newname(o_filename, sn + 1 ) else : new_filename = o_filename return new_filename if __name__ = = '__main__' : filename = "mks_data.txt" dir_path = 'download' dir_path = get_dirpath(dir_path) filename = f '{dir_path}/{get_newname(filename)}' for n in range ( 1 , 5 + 1 ): new_filename = get_newname(filename) f = open (new_filename, "w" ) f.write( str (n)) f.close() |
![[Python3] 檢查檔案及目錄是否存在 ,自動更名,避免複寫。](https://i0.wp.com/mks.tw/wp-content/uploads/2020/04/177378ddfcf312660c4ef0c8c311641c.png?resize=529%2C468&ssl=1)
補充資訊
檔案讀寫模式
1 2 3 4 5 6 7 8 9 | | r r+ w w+ a a+ ------------------|-------------------------- read | + + + + write | + + + + + write after seek | + + + create | + + + + truncate | + + position at start | + + + + position at end | + + |
該圖表參考 [2]
若是模式後方有 b
代表使用 Binary 模式操作檔案。
Reference
h t t p s : / / w w w . o p e n c l i . c o m / p e r l / p y t h o n – 4 – w a y – w r i t e – t o – f i l e
[2] h t t p s : / / s t a c k o v e r f l o w . c o m / q u e s t i o n s / 1 4 6 6 0 0 0 / p y t h o n – o p e n – b u i l t – i n – f u n c t i o n – d i f f e r e n c e – b e t w e e n – m o d e s – a – a – w – w – a n d – r
h t t p s : / / w w w . j i s h u w e n . c o m /d/2LwL/zh-tw
[4] https://shazi.info/python3-用-random-和-string-建立隨機字元-學習筆記/