Python csv 模組的檔案開檔時為什麼要指定 newline 參數為 ''?
Python 中使用 csv 模組寫入 CSV 格式資料時,都會要你在開檔時傳入 '' 給 newline 參數,避免多出空白行,這篇文章就來找出原因,才不會搞不清楚為什麼? 寫入檔案時對換行的操作 當你使用 write 或是 writelines 寫入到檔案時,會進行換行字元的處理: newline 換行處理 None(預設值) 把欲寫入檔案文字中的 '\n' 換成系統預設的換行字元,Windows 上就是 '\r\n',Linux 上就是 '\n' 空字串 '' 或 '\n' 不轉換,保留 '\n' 字元 '\r' 或 '\r\n' 把 '\n' 換成指定字串 以底下的程式碼為例: with open('file.txt', 'w') as file: file.write('Hello, world!\n') file.write('This is a test file.') 在 Windows 平台執行後以 16 進位查看: Format-Hex -Path file.txt Label: C:\Users\meebo\code\python\test_py3.13\file.txt Offset Bytes Ascii 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ------ ----------------------------------------------- ----- 0000000000000000 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 0D 0A 54 Hello, world!��T 0000000000000010 68 69 73 20 69 73 20 61 20 74 65 73 74 20 66 69 his is a test fi 0000000000000020 6C 65 2E le. 可以明確看到 '\n' 被換成 '\x0D\x0A,也就是 '\r\n'。但如果是在 linux 平台,則會看到: hexdump -C file.txt 00000000 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a 54 68 |Hello, world!.Th| 00000010 69 73 20 69 73 20 61 20 74 65 73 74 20 66 69 6c |is is a test fil| 00000020 65 2e |e.| 00000022 你可以看到 '\n' 對應到 '\x0a',也就是沒有轉換,仍然保留是 '\n'。 如果開檔時設定 newline 為 '',Windows 平台上看到的就會是: Format-Hex -Path file.txt Label: C:\Users\meebo\code\python\test_py3.13\file.txt Offset Bytes Ascii 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ------ ----------------------------------------------- ----- 0000000000000000 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 0A 54 68 Hello, world!�Th 0000000000000010 69 73 20 69 73 20 61 20 74 65 73 74 20 66 69 6C is is a test fil 0000000000000020 65 2E e. 你會發現原來被換成 '\r\n' 的地方現在只有 '\n' 了,這就是 ‵newline‵ 在寫入檔案時的作用。 讀入檔案時換行的處理 newline 換行處理 None(預設值) 啟用所謂的宇宙換行模式,文字中出現的 '\r'、'\n' 或是 '\r\n',都會被轉換成 '\n' 空字串 '' 使用宇宙換行模式認得所有的換行字元,但保留原樣不轉換 '\r'、'\r' 或 '\r\n' 只會認定指定的換行字元,但不會轉換,這會影響到計算的行數 以底下這個程式為例: with open('file.txt', 'w', newline='') as file: file.write('Hello, world!\n') file.write('This is a test file.\r\n') file.write('Another line\r') file.write('Last line') 我們刻意指定不轉換換行字元寫入檔案,以 16 進位格式檢視檔案內容: Format-Hex -Path file.txt Label: C:\Users\meebo\code\python\test_py3.13\file.txt Offset Bytes Ascii 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ------ ----------------------------------------------- ----- 0000000000000000 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 0A 54 68 Hello, world!�Th 0000000000000010 69 73 20 69 73 20 61 20 74 65 73 74 20 66 69 6C is is a test fil 0000000000000020 65 2E 0D 0A 41 6E 6F 74 68 65 72 20 6C 69 6E 65 e.��Another line 0000000000000030 0D 4C 61 73 74 20 6C 69 6E 65 �Last line 確實可以看到不同的換行字元都保留原樣在檔案中。如果以如下程式讀取同一個檔案: with open('file.txt', 'r') as file: for i, line in enumerate(file): print(f"Line {i}: {line}", end='') 會得到如下的結果: Line 0: Hello, world! Line 1: This is a test file. Line 2: Another line Line 3: Last line 表示不論檔案中是 '\n'、'\r' 還是 '\r\n',都會被當成換行,所以讀取結果總共有 4 行。但如果開檔時傳入 '\r' 給 newline,結果如下: Line 0: Hello, world! Line 1: a test file. Line 2: Last line 你會看到只有 3 行,這是因為以 '\r' 為換行字元,會讀到以下三行: 'Hello, world!\nThis is a test file.\r' '\nAnother line\r' 'Last line' 印第 1 行時結果為: Line 0: Hello, world! This is a test file. ^ 而且因為行尾的 \r,所以列印位置回到開頭的 'T' 處,接著印第 2 行就會蓋掉 'This...' 這一行開頭,變成: Line 0: Hello, world! Line 1: a test file. Another line ^ 'Line 1: ' 蓋掉了 'This is ',然後因為第 2 行開頭的 '\n' 導致列印位置移到下一行開頭,印出 'Another Line' 後,又因為 '\r' 把顯示位置移到這行開頭 'A' 處。再印出最後一行: Line 0: Hello, world! Line 1: a test file. Line 3: Last line 'Line 3: ' 一樣蓋掉了原本的 'Another '。 csv 模組在檔案寫入/讀取時對換行的處理 csv 模組在寫入檔案時,會依據所選用的 csv 方言,預設為 'excel',幫你在每一列資料最後加上該方言的換行字元,預設的 'excel' 方言換行字元就是 '\r\n';讀取 csv 檔案時,則是固定將 '\r' 與 '\n' 視為換行字元。我們以底下的程式為例: import csv with open('file.csv', 'w') as file: writer = csv.writer(file) writer.writerow(['Name', 'Age']) writer.writerow(['Alice', 30]) writer.writerow(['Bob', 25]) 在 Windows 上實際儲存的 csv 檔若以 16 進位格式查看: Format-Hex -Path file.csv Label: C:\Users\meebo\code\python\test_py3.13\file.csv Offset Bytes Ascii 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ------ ----------------------------------------------- ----- 0000000000000000 4E 61 6D 65 2C 41 67 65 0D 0D 0A 41 6C 69 63 65 Name,Age���Alice 0000000000000010 2C 33 30 0D 0D 0A 42 6F 62 2C 32 35 0D 0D 0A ,30���Bob,25��� 你會看到每一行的結尾都變成 '\r\r\n',這是因為 csv 模組會以 '\r\n' 當結尾,而 write 又把 '\n' 轉換成 '\r\n' 所造成。 如果以底下的程式碼讀取 csv 檔: import csv with open('fil

Python 中使用 csv 模組寫入 CSV 格式資料時,都會要你在開檔時傳入 '' 給 newline
參數,避免多出空白行,這篇文章就來找出原因,才不會搞不清楚為什麼?
寫入檔案時對換行的操作
當你使用 write 或是 writelines 寫入到檔案時,會進行換行字元的處理:
newline | 換行處理 |
---|---|
None(預設值) | 把欲寫入檔案文字中的 '\n' 換成系統預設的換行字元,Windows 上就是 '\r\n',Linux 上就是 '\n' |
空字串 '' 或 '\n' | 不轉換,保留 '\n' 字元 |
'\r' 或 '\r\n' | 把 '\n' 換成指定字串 |
以底下的程式碼為例:
with open('file.txt', 'w') as file:
file.write('Hello, world!\n')
file.write('This is a test file.')
在 Windows 平台執行後以 16 進位查看:
Format-Hex -Path file.txt
Label: C:\Users\meebo\code\python\test_py3.13\file.txt
Offset Bytes Ascii
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------ ----------------------------------------------- -----
0000000000000000 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 0D 0A 54 Hello, world!��T
0000000000000010 68 69 73 20 69 73 20 61 20 74 65 73 74 20 66 69 his is a test fi
0000000000000020 6C 65 2E le.
可以明確看到 '\n' 被換成 '\x0D\x0A,也就是 '\r\n'。但如果是在 linux 平台,則會看到:
hexdump -C file.txt
00000000 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a 54 68 |Hello, world!.Th|
00000010 69 73 20 69 73 20 61 20 74 65 73 74 20 66 69 6c |is is a test fil|
00000020 65 2e |e.|
00000022
你可以看到 '\n' 對應到 '\x0a',也就是沒有轉換,仍然保留是 '\n'。
如果開檔時設定 newline
為 '',Windows 平台上看到的就會是:
Format-Hex -Path file.txt
Label: C:\Users\meebo\code\python\test_py3.13\file.txt
Offset Bytes Ascii
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------ ----------------------------------------------- -----
0000000000000000 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 0A 54 68 Hello, world!�Th
0000000000000010 69 73 20 69 73 20 61 20 74 65 73 74 20 66 69 6C is is a test fil
0000000000000020 65 2E e.
你會發現原來被換成 '\r\n' 的地方現在只有 '\n' 了,這就是 ‵newline‵ 在寫入檔案時的作用。
讀入檔案時換行的處理
newline | 換行處理 |
---|---|
None(預設值) | 啟用所謂的宇宙換行模式,文字中出現的 '\r'、'\n' 或是 '\r\n',都會被轉換成 '\n' |
空字串 '' | 使用宇宙換行模式認得所有的換行字元,但保留原樣不轉換 |
'\r'、'\r' 或 '\r\n' | 只會認定指定的換行字元,但不會轉換,這會影響到計算的行數 |
以底下這個程式為例:
with open('file.txt', 'w', newline='') as file:
file.write('Hello, world!\n')
file.write('This is a test file.\r\n')
file.write('Another line\r')
file.write('Last line')
我們刻意指定不轉換換行字元寫入檔案,以 16 進位格式檢視檔案內容:
Format-Hex -Path file.txt
Label: C:\Users\meebo\code\python\test_py3.13\file.txt
Offset Bytes Ascii
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------ ----------------------------------------------- -----
0000000000000000 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 0A 54 68 Hello, world!�Th
0000000000000010 69 73 20 69 73 20 61 20 74 65 73 74 20 66 69 6C is is a test fil
0000000000000020 65 2E 0D 0A 41 6E 6F 74 68 65 72 20 6C 69 6E 65 e.��Another line
0000000000000030 0D 4C 61 73 74 20 6C 69 6E 65 �Last line
確實可以看到不同的換行字元都保留原樣在檔案中。如果以如下程式讀取同一個檔案:
with open('file.txt', 'r') as file:
for i, line in enumerate(file):
print(f"Line {i}: {line}", end='')
會得到如下的結果:
Line 0: Hello, world!
Line 1: This is a test file.
Line 2: Another line
Line 3: Last line
表示不論檔案中是 '\n'、'\r' 還是 '\r\n',都會被當成換行,所以讀取結果總共有 4 行。但如果開檔時傳入 '\r' 給 newline
,結果如下:
Line 0: Hello, world!
Line 1: a test file.
Line 2: Last line
你會看到只有 3 行,這是因為以 '\r' 為換行字元,會讀到以下三行:
'Hello, world!\nThis is a test file.\r'
'\nAnother line\r'
'Last line'
印第 1 行時結果為:
Line 0: Hello, world!
This is a test file.
^
而且因為行尾的 \r
,所以列印位置回到開頭的 'T' 處,接著印第 2 行就會蓋掉 'This...' 這一行開頭,變成:
Line 0: Hello, world!
Line 1: a test file.
Another line
^
'Line 1: ' 蓋掉了 'This is ',然後因為第 2 行開頭的 '\n' 導致列印位置移到下一行開頭,印出 'Another Line' 後,又因為 '\r' 把顯示位置移到這行開頭 'A' 處。再印出最後一行:
Line 0: Hello, world!
Line 1: a test file.
Line 3: Last line
'Line 3: ' 一樣蓋掉了原本的 'Another '。
csv 模組在檔案寫入/讀取時對換行的處理
csv 模組在寫入檔案時,會依據所選用的 csv 方言,預設為 'excel',幫你在每一列資料最後加上該方言的換行字元,預設的 'excel' 方言換行字元就是 '\r\n';讀取 csv 檔案時,則是固定將 '\r' 與 '\n' 視為換行字元。我們以底下的程式為例:
import csv
with open('file.csv', 'w') as file:
writer = csv.writer(file)
writer.writerow(['Name', 'Age'])
writer.writerow(['Alice', 30])
writer.writerow(['Bob', 25])
在 Windows 上實際儲存的 csv 檔若以 16 進位格式查看:
Format-Hex -Path file.csv
Label: C:\Users\meebo\code\python\test_py3.13\file.csv
Offset Bytes Ascii
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------ ----------------------------------------------- -----
0000000000000000 4E 61 6D 65 2C 41 67 65 0D 0D 0A 41 6C 69 63 65 Name,Age���Alice
0000000000000010 2C 33 30 0D 0D 0A 42 6F 62 2C 32 35 0D 0D 0A ,30���Bob,25���
你會看到每一行的結尾都變成 '\r\r\n',這是因為 csv 模組會以 '\r\n' 當結尾,而 write
又把 '\n' 轉換成 '\r\n' 所造成。
如果以底下的程式碼讀取 csv 檔:
import csv
with open('file.csv', 'r') as file:
reader = csv.reader(file)
for i, row in enumerate(reader):
print(f'Row {i}: {row}')
會得到以下結果:
Row 0: ['Name', 'Age']
Row 1: []
Row 2: ['Alice', '30']
Row 3: []
Row 4: ['Bob', '25']
Row 5: []
這是因為讀入檔案時,會先把 '\r\r\n' 裡的 '\r\n' 換成 '\n',變成 '\r\n',但是 csv 讀檔會把 '\r' 和 '\n' 都當成換行,所以你可以看到每一列資料後面都會有一列空的資料。
如果一開始存檔的時候,傳入 '' 給 newline
,那麼 csv 模組寫入檔案的 '\r\n' 就會保留原樣,像是這樣:
Format-Hex -Path file.csv
Label: C:\Users\meebo\code\python\test_py3.13\file.csv
Offset Bytes Ascii
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------ ----------------------------------------------- -----
0000000000000000 4E 61 6D 65 2C 41 67 65 0D 0A 41 6C 69 63 65 2C Name,Age��Alice,
0000000000000010 33 30 0D 0A 42 6F 62 2C 32 35 0D 0A 30��Bob,25��
讀取檔案時,會先將 '\r\n' 轉換成 '\n',csv 讀取時就視為單一換行,得到以下結果:
Row 0: ['Name', 'Age']
Row 1: ['Alice', '30']
Row 2: ['Bob', '25']
如果是在 Linux 上,因為預設的換行字元就是 '\n',因此即使傳入 '' 給 newline
開啟檔案,也不會更改 csv 模組寫入的 '\r\n',因此不會有問題。
透過以上的說明,你應該就瞭解了為什麼使用 csv 模組時,通常會要求你開檔時傳入 '' 給 newline
參數了。