24. CSV Serialization

24.1. Reading data from CSV files

  • Good practice is to always set:

    • encoding='utf-8'
    • quotechar='"'
    • delimiter=','
Code Listing 24.1. Zapis do plików csv używając csv.DictReader()
import csv

FILENAME = r'filename.csv'
"""
    "first_name", "last_name"
    "José", "Jiménez"
    "Matt", "Kowalski"
    "Иван", "Иванович"
    "Mark", "Watney"
"""


with open(FILENAME, encoding='utf-8') as file:
    data = csv.DictReader(file, delimiter=',', quotechar='"')

    for row in data:
        print(row['first_name'], row['last_name'])

24.2. Writing to CSV files

  • Good practice is to always set:

    • encoding='utf-8'
    • quoting=csv.QUOTE_ALL
    • quotechar='"'
    • delimiter=','
    • lineterminator='\n'
Code Listing 24.2. Zapis do plików csv używając csv.DictWriter()
import csv

FILENAME = r'filename.csv'
DATA = [
    {'first_name': 'José', 'last_name': 'Jiménez'},
    {'first_name': 'Mark', 'last_name': 'Watney'},
    {'first_name': 'Иван', 'last_name': 'Иванович'},
    {'first_name': 'Alex', 'last_name': 'Vogel'},
]


with open(FILENAME, mode='w', encoding='utf-8') as file:
    writer = csv.DictWriter(
        file,
        fieldnames=['first_name', 'last_name'],
        delimiter=',',
        quotechar='"',
        quoting=csv.QUOTE_ALL,
        lineterminator='\n')

    writer.writeheader()

    for row in DATA:
        writer.writerow(row)

24.3. Parsing non-CSV files with csv.DictReader()

24.3.1. Parsing /etc/passwd

Code Listing 24.3. Parsing /etc/passwd file with csv.DictReader()
import csv

FILENAME = r'etc-passwd.txt'
"""
root:x:0:0:root:/root:/bin/bash
watney:x:1000:1000:Mark Watney:/home/watney:/bin/bash
jimenez:x:1001:1001:José Jiménez:/home/jimenez:/bin/bash
ivanovic:x:1002:1002:Иван Иванович:/home/ivanovic:/bin/bash
"""

with open(FILENAME, encoding='utf-8') as file:
    fieldnames = ['username', 'password', 'uid', 'gid', 'full_name', 'home', 'shell']
    content = csv.DictReader(file, fieldnames=fieldnames, delimiter=':')

    for row in content:
        username = row['username']
        full_name = row['full_name']
        home = row['home']

        print(f'{username} -> {full_name} with HOME="{home}" ')

# root -> root with HOME="/root"
# watney -> Mark Watney with HOME="/home/watney"
# jimenez -> José Jiménez with HOME="/home/jimenez"
# ivanovic -> Иван Иванович with HOME="/home/ivanovic"

24.3.2. Parsing Java properties file

Code Listing 24.4. Parsing sonar-project.properties file with csv.DictReader()
import csv

FILENAME = r'sonar-project.properties'
"""
    sonar.host.url=https://sonarcloud.io
    sonar.language=py
    sonar.sourceEncoding=UTF-8
    sonar.verbose=true
    sonar.projectKey=habitatOS
    sonar.projectName=habitatOS
    sonar.projectDescription=Operating System for analog extraterrestrial habitats.
"""

with open(FILENAME) as file:
    config = csv.DictReader(
        file,
        fieldnames=['property', 'value'],
        delimiter='=',
        lineterminator='\n',
        quoting=csv.QUOTE_NONE)

    for line in config:
        property = line['property']
        value = line['value']
        print(f'{property} -> {value}')

24.4. Assignments

24.4.1. Reading csv

  1. Otwórz w przeglądarce podany powyżej URL

  2. Zapisz jego zawartość na dysku w miejscu gdzie masz skrypty w pliku iris-dataset.csv

  3. Korzystając z csv.DictReader wczytaj zawartość pliku

  4. Podaj jawnie encoding, delimiter oraz quotechar

  5. Pierwsza linijka stanowi metadane (nie wyświetlaj jej)

  6. Nazwy poszczególnych kolumn:

    • Sepal length
    • Sepal width
    • Petal length
    • Petal width
    • Species
  7. Kolumna species ma mieć wartość nazwy gatunku a nie cyfry 0, 1, 2 jak to jest w pliku

  8. Wypisz wiersze na ekranie

About:
  • Filename: csv_dictreader.py
  • Lines of code to write: 20 lines
  • Estimated time of completion: 15 min

24.4.2. Writing csv

  1. Za pomocą csv.DictWriter() zapisz do pliku dane o zmiennej strukturze
  2. Podaj jawnie encoding, delimiter, quotechar quoting, lineterminator
  3. fieldnames nie może być wymienione wprost w skrypcie (zahardkodowane)
DATA = [
    {'first_name': 'José'},
    {'last_name': 'Jiménez'},
    {'first_name': 'Иван', 'last_name': 'Иванович'},
    {'first_name': 'Mark', 'last_name': 'Watney', 'born': 1961},
    {'first_name': 'José', 'born': 1961, 'first_step': 1969},
]
About:
  • Filename: csv_dictwriter.py
  • Lines of code to write: 8 lines
  • Estimated time of completion: 15 min
Hints:
  • To jest bardzo często występujący i użyteczny przykład
The whys and wherefores:
 
  • Umiejętność korzystania z modułu csv
  • Umiejętność iteracji po złożonych strukturach danych
  • Dynamiczne generowanie struktur danych na podstawie innych

24.4.3. Object serialization to CSV

  1. Użyj obiektu książka_adresowa z listingu Code Listing 24.5.
  2. Za pomocą csv.DictWriter() zapisz kontakty z książki adresowej w pliku
  3. Wszystkie pola muszą być zawsze w cudzysłowiach i oddzielone średnikami, kodowanie UTF-8.
  4. Jak zapisać w CSV dane relacyjne (kontakt ma wiele adresów)?
  5. Stwórz obiekty książki adresowej na podstawie danych odczytanych z pliku
Hints:
  • powtarzanie rekordów (user pozostaje ten sam) z innymi danymi adresowymi
  • dodawanie kolumn (ulica_1, miasto_1, panstwo_1, ulica_2, miasto_2, panstwo_2) i automatyczne generowanie fieldnames
  • wrzucenie danych jako string do jednego pola adres_1, adres_2, adres_3 i ustalenie separatora (np: średnik - ‘;’)
  • jedno pole adres (w ramach niego wszystkie adresy rozdzielone “;” a dane przecinkami “,”)
Code Listing 24.5. Address book
class Contact:
    def __init__(self, first_name, last_name, addresses=[]):
        self.first_name = first_name
        self.last_name = last_name
        self.addresses = addresses


class Address:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)


addressbook = [
    Contact(first_name='Matt', last_name='Kowalski', addresses=[
        Address(street='2101 E NASA Pkwy', city='Houston', state='Texas', code='77058', country='USA'),
        Address(street=None, city='Kennedy Space Center', code='32899', country='USA'),
        Address(street='4800 Oak Grove Dr', city='Pasadena', code='91109', country='USA'),
        Address(street='2825 E Ave P', city='Palmdale', state='California', code='93550', country='USA', data_urodzenia=None),
    ]),
    Contact(first_name='José', last_name='Jiménez'),
    Contact(first_name='Иван', last_name='Иванович', addresses=[]),
]
About:
  • Filename: csv_addressbook.py
  • Lines of code to write: 10 lines
  • Estimated time of completion: 20 min