Python CSV Reader/Writer 例子


我的網站

CSV(comma-separated values) 是跨多種形式導入導出數據的標準格式,比如 MySQL、Excel。

它以純文本存儲數和文本。文件的每一行就代表一條數據,每條記錄包含了由逗號分隔的一個或多個屬性值。這個標準格式的名字來源就是每條記錄是用逗號將其屬性分隔的。

即使有這個非常明顯的命名標準,而實際上卻沒有一個官方的標準 CSV 格式,有可能是以一些比較類似的分隔符來分隔數據,雖然它們的擴展是 .csv 但是實際上卻用了各種符號,空格、tab,這些也是比較流行的符號。有時由於缺少嚴格的定義使得數據移植變得很困難。

RFC 4180 提供了一些標準:

  • 內容爲純文本
  • 包含記錄
  • 每條記錄是用單個分隔字符將各屬性分隔
  • 每條記錄的屬性序列是相同的

除非提供的 CSV 文件有額外的說明信息(比如使用 RFC 提供的標準),否則處理這種數據格式將是相當煩人的。

1.基礎

Python 天生支持讀取 CSV 格式數據並且是可配置的(這個我們看到是必不可少的)。在 Python 裏邊有個模塊 csv ,它包含了 CSV 讀取/生成所需的所有支持,並且它遵守 RFC 標準(除非你覆蓋了相應的配置),因此默認情況下它是能夠讀取和生成合法的 CSV 文件。

那麼,我們看看它是如何工作的:

import csv
with open('my.csv', 'r+', newline='') as csv_file:
    reader = csv.reader(csv_file)
    for row in reader:
        print(str(row))

代碼中我們導入了 csv 模塊並且打開了 "my.csv" 文件,將文件作爲參數傳給 csv.reader,調用這個方法後我們將 reader 裏邊的每行數據輸出。


假設 'my.csv' 裏邊的內容爲:


my first column,my second column,my third column
my first column 2,my second column 2,my third column 2

 

那麼我們運行這個代碼後,相應的輸出:


['my first column', 'my second column', 'my third column']
['my first column 2', 'my second column 2', 'my third column 2']


生成和讀取一樣的簡單:

import csv
rows = [['1', '2', '3'], ['4', '5', '6']]
with open('my.csv', 'w+', newline='') as csv_file:
    writer = csv.writer(csv_file)
    for row in rows:
        writer.writerow(row)

with open('my.csv', 'r+', newline='') as csv_file:
    reader = csv.reader(csv_file)
    for row in reader:
        print(str(row))


在 csv 文件的數據會是:

1,2,3
4,5,6

輸出的內容:


['1', '2', '3']
['4', '5', '6']

到這裏我們都非常清楚代碼邏輯。我們將文件作爲參數給 writer,以寫模式打開,然後用它來寫每一行數據。下邊是更靈活的方式:

import csv

def read(file_location):
    with open(file_location, 'r+', newline='') as csv_file:
        reader = csv.reader(csv_file)
        return [row for row in reader]

def write(file_location, rows):
    with open(file_location, 'w+', newline='') as csv_file:
        writer = csv.writer(csv_file)
        for row in rows:
            writer.writerow(row)

def raw_test():
    columns = int(input("How many columns do you want to write? "))
    input_rows = []
    keep_going = True
    while keep_going:
        input_rows.append([input("column {}: ".format(i + 1)) for i in range(0, columns)])
        ui_keep_going = input("continue? (y/N): ")
        if ui_keep_going != "y":
            keep_going = False

    print(str(input_rows))

    write('raw.csv', input_rows)
    written_value = read('raw.csv')
    print(str(written_value))

raw_test()


我們詢問用戶每行需要的列數然後是否繼續輸入下一行,隨後我們輸出讀入的一般數據並且將這些內容生成到一個叫 raw.csv,然後繼續這樣一個過程。當我們運行這個程序,相關的內容如下:


How many columns do you want to write? 3
column 1: row 1, column 1
column 2: row 1, column 2
column 3: row 1, column 3
continue? (y/N): y
column 1: row 2, column 1
column 2: row 2, column 2
column 3: row 3, column 3
continue? (y/N): 
[['row 1, column 1', 'row 1, column 2', 'row 1, column 3'], ['row 2, column 1', 'row 2, column 2', 'row 3, column 3']]
[['row 1, column 1', 'row 1, column 2', 'row 1, column 3'], ['row 2, column 1', 'row 2, column 2', 'row 3, column 3']]

Process finished with exit code 0

當然我們的 raw.csv 內容是:


"row 1, column 1","row 1, column 2","row 1, column 3"
"row 2, column 1","row 2, column 2","row 3, column 3"

CSV 格式的另一個規則就是引號。在上邊的例子中,每一個輸入都包含一個逗號,而逗號是我們的分隔符,所以這個逗號被放到了引號(標準的默認值)中間表示在此間的並非分隔符而只是每列中的符號而已。

雖讓我推薦保留標準的配置信息,不過也存在一些情況需要更改默認配置,畢竟你沒法控制的數據提供方給出的 csv 文件。因此,我不得不教你如何調整配置(beware, 責任越大權力越大)。

你能夠通過設置 delimiter 和 quotechar 來配置分隔符和引用符:


import csv


def read(file_location):
    with open(file_location, 'r+', newline='') as csv_file:
        reader = csv.reader(csv_file, delimiter=' ', quotechar='|')
        return [row for row in reader]


def write(file_location, rows):
    with open(file_location, 'w+', newline='') as csv_file:
        writer = csv.writer(csv_file, delimiter=' ', quotechar='|')
        for row in rows:
            writer.writerow(row)


def raw_test():
    columns = int(input("How many columns do you want to write? "))
    input_rows = []
    keep_going = True
    while keep_going:
        input_rows.append([input("column {}: ".format(i + 1)) for i in range(0, columns)])
        ui_keep_going = input("continue? (y/N): ")
        if ui_keep_going != "y":
            keep_going = False

    print(str(input_rows))

    write('raw.csv', input_rows)
    written_value = read('raw.csv')
    print(str(written_value))

raw_test()

現在我們的輸出值爲:


How many columns do you want to write? 3
column 1: row 1 column 1
column 2: row 1 column 2
column 3: row 1 column 3
continue? (y/N): y
column 1: row 2 column 1
column 2: row 2 column 2
column 3: row 2 column 3
continue? (y/N): 
[['row 1 column 1', 'row 1 column 2', 'row 1 column 3'], ['row 2 column 1', 'row 2 column 2', 'row 2 column 3']]
[['row 1 column 1', 'row 1 column 2', 'row 1 column 3'], ['row 2 column 1', 'row 2 column 2', 'row 2 column 3']]

我們的 raw.csv 文件中結果是:


|row 1 column 1| |row 1 column 2| |row 1 column 3|
|row 2 column 1| |row 2 column 2| |row 2 column 3|

我們可以看到,新分隔符已經變成了空格,而我們的引用符變成了管道,這主要是空格在每個文本數據中都有包含,因此 writer 強制使用管道來劃分。

writer 的引用策略同樣是可配置的,能夠用的參數如下:
 

  • csv.QUOTE_ALL: 把每列都擴起來,無論是否包含分隔符
  • csv.QUOTE_MINIMAL: 只將包含分隔符的列擴起來
  • csv.QUOTE_NONNUMBERIC: 將所有非數值的列擴起來
  • csv.QUOTE_NONE: 不擴起來。這種方式強制用戶檢查每列的輸入中是否包含分隔符,如果不檢測,讀取的列數將會是錯誤的。

讀/寫字點數據
 

我們已經看過了一些基礎的讀取/生成 CSV 數據的例子,不過在實際環境中,我們並不希望 CSV 文件那麼的混亂,我們需要它們給出裏邊每一個列所表示的意義。
 
同樣,實際環境下我們的數據不一定都是在數組中,我們有的是一些業務模型並且易懂。通常我們會使用字典來達到這個目的,python 相應提供了從 CVS 中讀取/生成字典的工具。
 
代碼差不多這個樣子:

import csv

dictionaries = [{'age': '30', 'name': 'John', 'last_name': 'Doe'}, {'age': '30', 'name': 'Jane', 'last_name': 'Doe'}]
with open('my.csv', 'w+') as csv_file:
    headers = [k for k in dictionaries[0]]
    writer = csv.DictWriter(csv_file, fieldnames=headers)
    writer.writeheader()
    for dictionary in dictionaries:
        writer.writerow(dictionary)

with open('my.csv', 'r+') as csv_file:
    reader = csv.DictReader(csv_file)
    print(str([row for row in reader]))

 


我們用一個數組的測試數據初始化 dictionaries 變量,然後我們打開一個 write 模式的文件,將字典數據中的鍵集合寫入到文件中。首先我們做的就是寫鍵集合,然後再寫數組中的字典,一個字典一行。
 
接下來我們以 read 模式打開同一個文件,獲取此文件的 reader,然後將其打印成數組。輸出結果如下:

[{'name': 'John', 'age': '30', 'last_name': 'Doe'}, {'name': 'Jane', 'age': '30', 'last_name': 'Doe'}]

 


輸出的 CSV 文件如下:

name,last_name,age
John,Doe,30
Jane,Doe,30

 


現在它看起來就舒服多了。這個 CSV 文件包含了列信息,每行按照列順序將數據排列好。注意到我們把屬性名字給了 writer,在 python 中字典是無序的,因此在給定的情況下每行的數據順序將會是之前所指定的。
 
這種方法同樣可以應用到配置了分隔符和引用符的默認 writer 和 reader 上。
 
同樣,我們把代碼變的更靈活些:


import csv

def read_dict(file_location):
    with open(file_location, 'r+') as csv_file:
        reader = csv.DictReader(csv_file)
        print(str([row for row in reader]))
        return [row for row in reader]


def write_dict(file_location, dictionaries):
    with open(file_location, 'w+') as csv_file:
        headers = [k for k in dictionaries[0]]
        writer = csv.DictWriter(csv_file, fieldnames=headers)
        writer.writeheader()
        for dictionary in dictionaries:
            writer.writerow(dictionary)


def dict_test():
    input_rows = []
    keep_going = True
    while keep_going:
        name = input("Name: ")
        last_name = input("Last Name: ")
        age = input("Age: ")
        input_rows.append({"name": name, "last_name": last_name, "age": age})
        ui_keep_going = input("continue? (y/N): ")
        if ui_keep_going != "y":
            keep_going = False

    print(str(input_rows))

    write_dict('dict.csv', input_rows)
    written_value = read_dict('dict.csv')
    print(str(written_value))

dict_test()

我們現在運行這個代碼,會有如下的結果:

Name: John
Last Name: Doe
Age: 30
continue? (y/N): y
Name: Jane
Last Name: Doe
Age: 40
continue? (y/N): 
[{'age': '30', 'last_name': 'Doe', 'name': 'John'}, {'age': '40', 'last_name': 'Doe', 'name': 'Jane'}]
[{'age': '30', 'last_name': 'Doe', 'name': 'John'}, {'age': '40', 'last_name': 'Doe', 'name': 'Jane'}]

 


我們的 dict.csv 內容:

age,last_name,name
30,Doe,John
40,Doe,Jane

 


一些注意點。就像我之前說過的,在 python 中字典是無序的,所以當你從一個字典中提取鍵集合並將數據寫入到一個 CSV 文件的時候你應該將它們排好序始終使它們保持同樣的順序。你並不知道你的用戶會用什麼方法來讀取這些數據,當然,在實際環境中 CSV 文件總是在增長的,你總是在增加文件的行,而不是覆蓋它們。爲了避免不必要的麻煩首先就是確保你的 CSV 文件始終看起來相同。

翻譯原文


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章