Menu

CSV w Pythonie: odczyt i zapis plików CSV modułem csv

Jak czytać i zapisywać pliki CSV w Pythonie: moduł csv, DictReader i DictWriter, obsługa nagłówków, cudzysłowy i kiedy lepiej sięgnąć po pandas.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

CSV jest prostszy, niż wygląda (i bardziej podstępny, niż się wydaje)

Plik CSV to zwykły tekst: wiersze oddzielone znakami nowej linii, pola oddzielone przecinkami. Taka jest idea. W praktyce trafiasz na napisy w cudzysłowach zawierające przecinki, pola z wbudowanymi znakami nowej linii, różne konwencje regionalne (przecinek albo średnik) i pliki zapisane w Excelu z BOM. Przypadków brzegowych jest tyle, że własne line.split(",") prawie zawsze okazuje się błędem.

Wbudowany moduł csv w Pythonie radzi sobie z tym wszystkim. Przy małych i średnich plikach rzadko będziesz potrzebować czegoś więcej.

Odczyt CSV przez csv.reader

csv.reader zwraca każdy wiersz jako listę napisów:

import csv

with open("people.csv", newline="") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

Kilka mniej oczywistych szczegółów:

  • Zawsze przekazuj newline="" do open. Moduł csv sam obsługuje końce linii, a bez tego na Windowsie dostaniesz dodatkowe puste wiersze.
  • Każda wartość to napis. "42" pozostaje napisem, dopóki nie wywołasz na nim int(...). CSV nie ma typów.
  • Wiersz nagłówków to po prostu kolejny wiersz. Jeśli plik ma nagłówki, pomiń pierwszy wiersz ręcznie albo przejdź na DictReader.

Pomijanie wiersza nagłówków

import csv

with open("people.csv", newline="") as f:
    reader = csv.reader(f)
    headers = next(reader)         # wyciąga pierwszy wiersz
    for row in reader:
        print(row)

next(reader) przesuwa iterator o jeden i zwraca ten wiersz.

Odczyt jako słowniki przez DictReader

csv.DictReader traktuje pierwszy wiersz jako nagłówki, a każdy kolejny wiersz daje jako słownik:

import csv

with open("people.csv", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row["name"], row["email"])

Prawie zawsze właśnie tego chcesz. Nazwy kolumn same się dokumentują, a zmiana kolejności kolumn w pliku źródłowym nie psuje twojego kodu.

Jeśli plik nie ma nagłówków, przekaż je jawnie przez fieldnames=["name", "email", ...].

Zapis CSV przez csv.writer

csv.writer zamienia wiersze (listy) na linie CSV:

import csv

rows = [
    ["name", "age", "city"],
    ["Rosa", 30, "Lisbon"],
    ["Ada", 36, "London"],
]

with open("out.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(rows)

writerow(row) zapisuje jeden wiersz, a writerows(rows) cały obiekt iterowalny naraz. Obie metody automatycznie biorą pola w cudzysłów, gdy zawierają przecinki, cudzysłowy albo znaki nowej linii, więc nie musisz o tym myśleć.

Zapis słowników przez DictWriter

Gdy dane masz już w słownikach, DictWriter pomija krok "zamień na listę":

import csv

people = [
    {"name": "Rosa", "age": 30, "city": "Lisbon"},
    {"name": "Ada", "age": 36, "city": "London"},
]

with open("out.csv", "w", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=["name", "age", "city"])
    writer.writeheader()
    writer.writerows(people)

Argument fieldnames decyduje zarówno o nagłówku, jak i o kolejności kolumn. Klucze ze słowników, których nie ma w fieldnames, są po cichu pomijane (albo możesz wymusić wyjątek przez extrasaction="raise").

Inne separatory i cudzysłowy

Nie każdy "CSV" używa przecinków. W Europie często używa się ;, pliki rozdzielane tabulatorami używają \t, a niektóre systemy |. Przekaż argument delimiter=:

import csv

with open("data.tsv", newline="") as f:
    reader = csv.reader(f, delimiter="\t")
    for row in reader:
        print(row)

Dla plików z nietypowymi regułami cudzysłowów csv.register_dialect(...) pozwala skonfigurować wszystko raz i używać wielokrotnie. Dla większości plików wystarczą ustawienia domyślne plus delimiter=.

Kodowanie

Pliki CSV to tekst, więc mają kodowanie. UTF-8 to współczesny standard, ale pliki z Excela na Windowsie czasem używają cp1252 albo zawierają BOM UTF-8. Podawaj kodowanie jawnie:

with open("data.csv", newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    ...

Jeśli widzisz UnicodeDecodeError, plik nie jest w kodowaniu, które zakładasz. Typowi podejrzani to utf-8-sig (obsługuje BOM z Excela), cp1252 i latin-1. Przy polskich plikach z Windowsa częstym winowajcą jest też cp1250.

Zamiana wierszy CSV na przydatne typy

Każda wartość przychodzi jako napis, więc parsowanie należy do ciebie:

(StringIO pozwala uruchomić przykład bez prawdziwego pliku. W prawdziwym kodzie użyjesz open(path).)

Dla plików CSV z trudnymi typami (daty, liczby, które mogą być puste, true/false zapisane na dziesięć sposobów) rozważ pandas, które ma wbudowane konwencje dla większości z nich.

Kiedy sięgnąć po pandas

pandas.read_csv(path) zwraca DataFrame, czyli właściwą strukturę, gdy tylko chcesz:

  • Filtrować wiersze: df[df["active"] == True]
  • Agregować: df.groupby("city")["age"].mean()
  • Łączyć z inną tabelą
  • Zapisywać wynik z prostym formatowaniem
import pandas as pd

df = pd.read_csv("people.csv")
adults = df[df["age"] >= 18]
adults.to_csv("adults.csv", index=False)

Pandas to przesada przy małych, liniowych odczytach i do tego ciężka zależność (zainstaluj ją przez pip w środowisku wirtualnym). Ale przy wszystkim, co ma kształt danych, to narzędzie, po które sięga większość analityków używających Pythona.

Strumieniowe przetwarzanie bardzo dużych plików

csv.reader już działa leniwie: czyta jeden wiersz naraz. Utrzymaj to, iterując (zamiast wywoływać na starcie list(reader)), a zużycie pamięci pozostanie stałe niezależnie od rozmiaru pliku:

import csv

with open("huge.csv", newline="") as f:
    reader = csv.DictReader(f)
    error_count = 0
    for row in reader:
        if row["status"] == "error":
            error_count += 1

print(f"Found {error_count} errors.")

Tak samo dobrze obsłuży to plik o rozmiarze 10 GB, jak plik 10 KB, o ile nie zbierasz wierszy w listę.

Kilka nawyków

  • Zawsze przekazuj newline="" do open przy odczycie i zapisie plików CSV.
  • Używaj DictReader/DictWriter, gdy plik ma nagłówki: to czytelniejsze niż indeksy liczbowe.
  • Podawaj kodowanie jawnie, zwłaszcza przy plikach z Excela albo ze źródeł nieanglojęzycznych.
  • Konwertuj typy od razu przy odczycie, żeby dalszy kod nie musiał tego robić.
  • Sięgaj po pandas, gdy chcesz dane analizować, a nie tylko przenosić.

Dalej

Umiesz już czytać JSON i CSV. Ostatnia praktyczna umiejętność, którą omówimy, to pobieranie danych przez sieć: o tym jest następna strona, poświęcona żądaniom HTTP z biblioteką requests.

Najczęściej zadawane pytania

Jak odczytać plik CSV w Pythonie?

Użyj wbudowanego modułu csv. csv.reader daje każdy wiersz jako listę napisów, a csv.DictReader traktuje pierwszy wiersz jako nagłówki i zwraca każdy wiersz jako słownik. Otwórz plik z newline='', żeby Python nie psuł znaków końca linii: with open('data.csv', newline='') as f:.

Jak zapisać plik CSV w Pythonie?

Połącz csv.writer z plikiem otwartym w trybie zapisu i z newline=''. Wywołuj writer.writerow([...]) dla każdego wiersza albo writer.writerows([[...], [...]]) dla całej paczki. Dla danych w słownikach użyj csv.DictWriter, który sam obsługuje nagłówki.

Moduł csv czy pandas?

Używaj csv do szybkich odczytów i zapisów, plików przetwarzanych wiersz po wierszu i gdy nie chcesz kolejnej zależności. Używaj pandas, gdy trzeba filtrować, grupować albo łączyć tabele, albo gdy plik jest na tyle duży, że operacje wektorowe mają znaczenie. Oba obsługują te same pliki, a wybór zależy od tego, co robisz z danymi po wczytaniu.

Dlaczego mój CSV ma puste linie między wierszami na Windowsie?

Plik został otwarty bez newline=''. Moduł csv zapisuje własne znaki końca linii, a bez tego argumentu Python dodaje na Windowsie dodatkowe. Zawsze otwieraj pliki CSV przez open(path, newline=''), zarówno do odczytu, jak i do zapisu.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ