Python 全攻略 第10節:Chapter 5- I/O in Python 82~87

82.介紹 I/O with file
83. readline, readlines, and close
84. Encoding 亂碼
85. with statement and modes
86. deleting files and folders
87. user input

Python I/O:程式與外部世界的橋樑

什麼是 I/O?

I/O(Input/Output),在這章節我們討論的

  • 輸入(Input): 將.txt檔案input並讀取/處理資料。
  • 輸出(Output): 將資料寫入/處理後並輸出.txt

Python 檔案處理方法:

功能 解釋 範例
開啟檔案open("filename.txt") filename 是一個string, 會回傳一個file object
開啟檔案open("filename.txt", "mode") mode常見模式有:r 讀取、w 覆寫、a 追加、x 創建新檔案、b 二進位模式。 file = open("filename.txt", "r")
讀取整個檔案 .read() 讀取檔案所有內容,返回為字串string。 words = file.read()
讀取指定數量的字元 .read(n) 讀取指定字元數量 n,返回為字串。 word = file.read(6)
讀取單行 .readline() 讀取下一行內容,返回為字串。 first_line = file.readline()
讀取所有行 .readlines() 讀取所有行並返回列表,每行作為列表的一個元素list。佔記憶體 lines = file.readlines()
寫入檔案 file =open('file.txt','w') as file: 將字串 str 寫入檔案(w覆蓋模式)。 file =open('file.txt','w') as f: f.write("Hello, world!\n")
寫入檔案 file =open('file.txt','a') as file: 使用'a' 模式來追加寫入並讀取(同一個檔案)不會覆蓋 with open('testFile.txt', 'a', encoding='utf-8') as f: f.write("Keep learning !\n")
寫入檔案 with open('my_file.txt', 'x') as f: ‘x’ 模式 的主要用途是 創建一個新檔案,如果檔案已經存在,就會報錯。因此,它並不能直接用於追加寫入和讀取同一個檔案。 with open('my_file.txt', 'x') as f: f.write("第一次寫入\n")
寫入多行 .writelines(seq) seq 序列中的每個元素作為一行寫入檔案,適用於列表或元組 lines = ["Line 1\n", "Line 2\n"]
file.writelines(lines)
使用 print() 寫入 print(data, file=file) 使用 print() 函數將 data 寫入到指定的檔案 file data = "Hello, world!"
print(data, file=file)
使用 with 自動關閉with open("filename.txt", "mode") as file 使用 with 開啟檔案,程式執行完畢後會自動關閉檔案。 with open("filename.txt", "w") as file:
file.write("Hello, world!")

開啟檔案open() & with open

基本語法

file = open("filename.txt", "mode")
file.close()

filename.txt: 要開啟的檔案名稱相對路徑。
mode: 開啟模式,常見的有:
r: 讀取 (預設)
w: 寫入 (覆寫)
a: 追加 (在檔案末尾追加)
x: 創建新檔案 (如果檔案已存在則報錯)

進階使用與 with 一起使用 開啟檔案

什麼是 with 陳述式?
with 陳述式是一種上下文管理器,它讓程式碼更簡潔,並且能自動釋放資源。
為什麼要用 with 陳述式?
在處理檔案、資料庫連接等資源時,為了避免忘記關閉檔案導致的資源洩露,使用 with 陳述式能確保資源在使用完畢後被自動正確關閉檔案,即使發生異常。

with 陳述式的語法

with open(expression,"mode") as variable:
    # 在這個區塊中,可以使用 variable 來訪問資源
  • expression:通常是一個函式呼叫,返回一個上下文管理器物件。
  • variable:一個變數,用來表示上下文管理器所管理的資源。
with open("my_file.txt", "a" as file:
    file.write("This is a new line.\n")

as file 中的 file 是自定義的
as file 中的 file 並不是 Python 的關鍵字,而是一個變量名稱。
你可以將它換成任何你喜歡的變量名稱

讀取檔案 "r"

一次讀取所有內容.read():

假設我有一份my_file.txt 裡面內容為:

I love python
I love Club
with open("my_file.txt","r") as file:
    words = file.read()
print(words) 

#輸出
I love python
I love Club

讀取指定數量的位元組.read(n):

with open("my_file.txt","r") as file:
    word = file.read(6)
print(word) 
file.close()
#輸出 I love

單行讀取.readline()

with open("my_file.txt","r") as file:
    first_line = file.readline()
    second_line = file.readline()
print(second_line,first_line,sep='\n') 

#輸出
I love Club
I love python

逐行讀取.readlines()

它會將文件中的每一行讀取為一個元素,並將這些元素存儲在一個列表中。

with open("my_file.txt","r") as file:
    a = file.readlines()
print(a) 

#輸出
['I love python\n', 
'I love Club']

# 遍歷每一行
with open("my_file.txt","r") as file:
    for line in file.readlines():
        print(line)

#輸出
I love python

I love Club

86.Bonus section 編碼 (encoding)

什麼是編碼(encoding)?

想像一下,我們人類用中文、英文、日文等不同的文字來溝通,而電腦只認識 0 和 1。
為了讓電腦能理解我們輸入的文字,就需要一種「翻譯」的方式,
將文字轉換成電腦能識別為一組數字,這個轉換的過程就稱為「編碼」。

簡單來說,編碼就是將人類可讀的文字轉換成電腦可讀的數字序列。
編碼是將文字轉換成電腦可以理解的二進位數字的過程。
不同的編碼方式使用不同的對應關係,例如 ASCII、UTF-8、Big5 等。

為什麼要了解編碼?

在讀取或開啟文件時,python會根據你電腦的os預設encoding來呈現,
但如果跟其他人寫入文件的encoding不同,就 有可能會看到亂碼
所以在處理文字檔案時,必須指定正確的編碼方式,才能正確地讀取和寫入資料。

常見的編碼:

  • UTF-8: 最常用,一種可變長度的 Unicode 轉碼格式,幾乎可以表示世界上所有的文字。
  • ASCII: 主要用於顯示現代英文的字元。
  • cp950: 常用於繁體中文的編碼。

結合範例

with open('my_file.txt', 'r', encoding='utf-8') as f:
    for line in f:
        print(line, end='')

寫入檔案

常見寫入檔案的模式

w: 寫入模式,如果檔案存在則會覆蓋原有檔案內容。如果檔案不存在,則會創建新檔案。
a: 附加模式,用於在檔案末尾追加內容。如果檔案不存在,則會創建新檔案。
x: 排他性創建模式,如果檔案已經存在則會報錯。
b: 二進位模式,用於讀寫二進位資料。
t: 文字模式(預設),用於讀寫文字資料。

使用'w' 模式寫入檔案的步驟

先打開檔案寫入,再讀取(分開操作,無法同時寫入和讀取)
原因: 在同一個 with 語句中,使用 ‘w’ 模式開啟檔案後,檔案就處於寫入狀態。如果你想在寫入之後立即讀取,需要重新開啟檔案,並使用 ‘r’ 模式。

# 以寫入模式開啟一個名為 testFile.txt 的檔案
with open('testFile.txt', 'w', encoding='utf-8') as file:
    #寫入檔案
    file.write("Hello, world!\n")  # 寫入一行文字
#再讀取
with open('testFile.txt', 'r', encoding='utf-8') as file:
    for line in file:
        print(line, end='')

這段程式碼做了以下事情:

  • 開啟檔案: 使用 with open() 開啟名為 my_file.txt 的檔案,以讀取模式開啟,並指定編碼為 UTF-8。
  • 逐行讀取: 使用 for 迴圈逐行讀取檔案內容,並將每一行印出。
  • 自動關閉: 離開 with 區塊時,檔案會自動關閉。

使用'a' 模式來追加寫入並讀取(同一個檔案)

# 追加寫入檔案
with open('my_file.txt', 'a', encoding='utf-8') as f:
    f.write("Hello, world!\n")

# 讀取檔案
with open('my_file.txt', 'r', encoding='utf-8') as f:
    for line in f:
        print(line, end='')

使用'x' 模式

‘x’ 模式 的主要用途是 創建一個新檔案,如果檔案已經存在,就會報錯。因此,它並不能直接用於追加寫入和讀取同一個檔案。

 with open('my_file.txt', 'x') as f:
        f.write("第一次寫入\n")
# 追加寫入        
 with open('my_file.txt', 'a') as f:
    f.write("追加內容\n")       
# 讀取
with open('my_file.txt', 'r') as f:
    for line in f:
        print(line, end='')        

其他寫入檔案的方式 .writelines(seq)

在 Python 中,當我們想要一次性將一個序列(例如列表或元組)中的多個字符串寫入到一個檔案中時,,並且在每個字符串的末尾自動添加一個換行符。.writelines(seq) 方法是一個非常方便的選擇。

with open("my_file.txt", "w", encoding='utf-8') as file:
    lines = ["第一行\n", "第二行\n", "第三行\n", "test\n"]
    file.writelines(lines)

其他寫入檔案的方式 print(data,file=f)

在 Python 中,print() 函數通常用來將資料輸出到終端(也就是我們平常看到的命令列)。然而,透過指定 file 參數,我們可以將輸出導向到一個檔案中,而不是顯示在螢幕上。
-data: 你想要寫入檔案的資料,可以是字符串、數字、或是更複雜的資料結構。
-file: 要寫入的檔案對象。這個對象通常是透過 open() 函數取得的。

with open('my_file.txt', 'w') as file:
    data = "Hello, world!\n"
    print(data, file=file)
with open('movieList.txt', 'w', encoding='utf-8')as f:
    movies = ['movie1', 'movie2', 'movie3']
    print(movies, file=f)

option

Python 的 seek() 方法:精準控制檔案讀寫位置

seek() 函數的用途

seek() 函數在 Python 中主要用於在檔案中移動讀寫位置。想像一個檔案就像是一本書,而 seek() 就如同將書籤移動到書的任意頁面。透過這個函數,我們可以精準地控制從檔案的哪個位置開始讀取或寫入資料。

seek() 的參數

offset: 偏移量,表示要移動的位元組數。可以是正數(向後移動)、負數(向前移動)或 0(無移動)。
whence: 可選參數,表示偏移量的參考位置。
file.seek(0) :檔案的起始位置
file.seek(1) : (os.SEEK_CUR):當前的文件位置
file.seek(2) : (os.SEEK_END):檔案的結尾位置

常見的使用情境

隨機存取檔案:

  • 從任意位置開始讀取: 將 offset 設為要開始讀取的位元組數,whence 設為 0。
  • 在特定位置插入資料: 將 offset 設為插入位置,whence 設為 0,然後使用 write() 方法寫入資料。

處理大型檔案:

  • 分段讀取: 對於過大的檔案,一次性讀取可能會導致記憶體不足。可以使用 seek() 分段讀取,每次只讀取一小部分。
  • 跳過無用資料: 如果檔案中包含大量無用的資料,可以使用 seek() 跳過這些部分,直接定位到需要的資料。
  • 修改檔案內容:
    • 替換特定內容: 將文件指針移動到要替換的內容的起始位置,然後使用 write() 方法寫入新的內容。
    • 插入新內容: 將文件指針移動到插入位置,然後使用 write() 方法寫入新的內容。
with open('my_file.txt', 'r+') as f:
    # 移動到檔案末尾
    f.seek(0, 2)
    # 在末尾追加一行
    f.write('\nThis is a new line.')

    # 移動到檔案開頭
    f.seek(0)
    # 讀取前10個位元組
    data = f.read(10)
    print(data)

注意事項

  • 二進位模式 vs. 文字模式: 在二進位模式下,seek() 操作的是位元組,而在文字模式下,seek() 操作的是字元。
  • 不同系統的換行符: 在不同作業系統中,換行符的表示方式不同。Windows 使用 \r\n,而 Unix/Linux 使用 \n。
  • 文件指針: seek() 操作會改變文件指針的位置,後續的讀寫操作都會從新的位置開始。
  • 檔案類型: 不是所有的檔案都支持隨機存取,例如壓縮檔案或資料庫檔案。

常見的應用場景:
編輯器: 在編輯器中,seek() 可以用於實現光標的移動和內容的插入、刪除。
資料庫: 資料庫系統中,seek() 可以用於定位到特定的記錄。
網路通訊: 在網路通訊中,seek() 可以用於處理接收到的資料包。

小結

seek() 函數為我們提供了對檔案進行精細控制的能力,在處理各種檔案操作時都非常有用。通過熟練掌握 seek() 的用法,我們可以更靈活地處理檔案資料。

86. Deleting Files and Folders

在 Python 中,您可以使用內建模組 os 和 shutil 來刪除檔案和資料夾。

以下是一些關鍵的概念和方法:

刪除檔案

使用 os.remove() 方法來刪除單一檔案。
範例程式碼

import os 

file_path = "example.txt" # 檔案路徑

# 檢查檔案是否存在,然後刪除
if os.path.exists(file_path):
       os.remove(file_path)
       print(f"{file_path} 已刪除")
else:
       print(f"{file_path} 不存在")

注意
如果檔案不存在且直接執行 os.remove(),會拋出 FileNotFoundError。
確保在執行刪除前,確認檔案路徑正確。

刪除空資料夾

使用 os.rmdir() 刪除空資料夾。
範例程式碼

import os

# 資料夾路徑
folder_path = "empty_folder"

# 檢查資料夾是否存在,然後刪除
if os.path.exists(folder_path):
      os.rmdir(folder_path)
      print(f"{folder_path} 已刪除")
else:
      print(f"{folder_path} 不存在")

os.rmdir() 只能刪除空資料夾。如果資料夾中有檔案,會引發 OSError。

刪除非空資料夾

使用 shutil.rmtree() 刪除資料夾及其內容(包含檔案和子資料夾)。
範例程式碼

import shutil

# 資料夾路徑
folder_path = "non_empty_folder"

# 檢查資料夾是否存在,然後刪除
if os.path.exists(folder_path):
       shutil.rmtree(folder_path)
       print(f"{folder_path} 已刪除,包括其中的所有內容")
else:
      print(f"{folder_path} 不存在")

注意: shutil.rmtree() 是強力刪除,會移除資料夾及其所有內容,請謹慎操作。如果要在刪除前詢問用戶確認,可以結合輸入功能。

其他實用技巧

刪除特定條件的檔案
可以結合 os.listdir() 和條件判斷來刪除特定檔案,例如刪除 .txt 檔案:

import os

folder_path = "example_folder"

# 列出資料夾中的檔案
for file_name in os.listdir(folder_path):
      if file_name.endswith(".txt"): # 找到所有 .txt 檔案 
           os.remove(os.path.join(folder_path, file_name))
print(f"{file_name} 已刪除")

避免潛在問題

  • 權限問題:確認執行程式的使用者具有刪除檔案/資料夾的權限。
  • 不存在的路徑:在操作前使用 os.path.exists() 確保路徑有效。
  • 不必要的遞迴操作:避免錯誤地刪除不應刪除的父資料夾。

87.User Input

1.Python input() 背後的操作

  • input() 等待用戶輸入數據。
  • 當用戶按下enter後,Python 讀取輸入並將其作為字串返回。
  • 與操作系統的關係
    • 在等待用戶輸入時,input() 會進入 I/O 操作階段。
    • 在操作系統層面,I/O 是一種慢速操作,因為它需要等待用戶交互完成

2. OS Blocked Stage

  • 當程式執行到 input() 時,會進入 I/O Blocked Stage
    • 什麼是 Blocked Stage
      • CPU 將進程暫停suspend,等待 I/O 完成(例如等待鍵盤輸入)。
      • 此時,該進程處於「阻塞(Blocked)」狀態,並讓出 CPU 資源。
    • 為什麼需要 Blocked
      • 用戶輸入的速度通常比 CPU 執行的速度慢得多。如果不進入阻塞狀態,CPU 資源會浪費在等待用戶操作上。

「suspend」:這個進程會被暫停,無法繼續執行,直到特定的事件或條件達成。例如,在等待用戶輸入數據時,程式進程會進入「掛起」狀態,直到用戶完成輸入並按下enter鍵,程式才會繼續執行。

3. CPU 和標準輸出(Standard Output)

  • 標準輸出 (stdout) 是指輸出數據到螢幕的過程,例如使用 print()
  • input() 配合時:
    1. 程式會先用標準輸出將提示文字顯示在螢幕上,告知用戶需要輸入什麼。
name = input("Enter your name: ")  # 標準輸出顯示提示文字

    1. 當用戶輸入時,鍵盤的數據經操作系統傳送到程式,程式接收後進入執行狀態。

4. 簡單流程總結

  1. input() 調用過程:
  • CPU 請求操作系統處理鍵盤輸入。
  • 程式進入 Blocked Stage,等待用戶輸入。
  • 用戶按下enter後,數據從鍵盤進入標準輸入,程式繼續執行。
  1. print() 調用過程:
  • CPU 請求操作系統將數據寫入螢幕。
  • 螢幕作為 I/O 設備,將數據顯示給用戶。

5. 關聯總結

  • input():涉及操作系統的 I/O 阻塞,程式在等待用戶輸入時,進程會被掛起,釋放 CPU。
  • print()(標準輸出):涉及操作系統的 I/O 輸出,數據經由操作系統傳輸到螢幕顯示。
  • CPU 與 I/O 的互動
    • 等待輸入時,程式進入阻塞狀態。
    • 標準輸出時,CPU 僅負責傳輸數據至 I/O 設備(螢幕)。

這種設計保證了系統資源的高效利用,避免了 CPU 的閒置浪費。

3個讚