Python Συνταγή 1 – Δουλεύοντας με csv αρχεία

Τα csv (comma seperated values) αρχεία είναι μία συνηθισμένη μορφή αρχείων την οποία συναντάμε συχνά κατά όταν θέλουμε να κάνουμε εισαγωγή δεδομένων στη βάση μας ή και εξαγωγή.

Πρόκειται για ένα αρχείο plaintext όπου κάθε μία γραμμή είναι μια εγγραφή η οποία περιέχει ένα ή περισσότερα πεδία τα οποία διαχωρίζονται μεταξύ τους συνήθως με το σύμβολο του κόμμα ” , ” . Επίσης τα πεδία συνήθως τα εσωκλείουμε σε εισαγωγικά είτε μονά είτε διπλά.

Το standard το οποίο χρησιμοποιούμε είναι το RFC 4180 το οποίο περιλαμβάνει τους παρακάτω κανόνες για την δομή ενός csv αρχείου :

  • Είναι plain text
  • Περιλαμβάνει εγγραφές όπου κάθε γραμμή είναι μία εγγραφή
  • Κάθε εγγραφή περιλαμβάνει πεδία τα οποία διαχωρίζονται μεταξύ τους με έναν συγκεκριμένο χαρακτήρα π.χ. ( , | ; )
  • Κάθε εγγραφή έχει την ίδια σειρά των πεδίων και τον ίδιο αριθμό

Η Python υποστηρίζει την επεξεργασία των csv αρχείων με το module csv. Στην 1η μας συνταγή λοιπόν θα δείξουμε πώς μπορούμε να διαβάσουμε ένα csv αλλά και πώς μπορούμε να γράψουμε σε αυτό.

Για τις ανάγκες των παραδειγμάτων θα χρησιμοποιήσουμε ένα sample csv το οποίο μπορείτε να κατεβάσετε δωρεάν από εδώ και περιλαμβάνει 500 γραμμές με demo data.

Ανάγνωση CSV αρχείου :

#Python Recipe 1
#CSV READER
import csv

with open('us-500.csv','r+',newline='') as csvFile:
    csvReader = csv.reader(csvFile)
    for row in csvReader:
        print(str(row))

Στον παραπάνω κώδικα ανοίγουμε το αρχείο us-500.csv . Όπως θα δούμε δίπλα από το αρχείο συμπληρώνουμε το flag r+, με το οποίο δηλώνουμε πώς θέλουμε να ανοίξουμε το αρχείο για ανάγνωση ενώ στο τέλος την παράμετρο newline = ” . Στη συνέχεια καλέσαμε την συνάρτηση του csv module την reader για το csvFile η οποία μας επιστρέφει ένα αντικείμενο με τα περιεχόμενα του αρχείου και το οποίο μπορούμε να το προσπελάσουμε ανά γραμμή μιας και υποστηρίζει το iterator protocol.

Σύμφωνα με το documentation όταν το csv το ανοίγουμε σαν αντικείμενο αρχείου (file object) θα πρέπει να το βάζουμε με αυτό τον τρόπο. Εάν το παραλείψουμε υπάρχει κίνδυνος να μην διαβαστεί σωστά το αρχείο καθώς αρκετές πλατφόρμες χρησιμοποιούν δικά τους escape characters όπως το \r\n .

Εαν εκτελέσουμε τον παραπάνω κώδικα παίρνουμε το παρακάτω output (θα γράψω μόνο τις δύο τελευταίες γραμμές) :

$ python csv-reader.py
['Jani', 'Biddy', 'Warehouse Office & Paper Prod', '61556 W 20th Ave', 'Seattle', 'King', 'WA', '98104', '206-711-6498', '206-395-6284', 'jbiddy@yahoo.com', 'http://www.warehouseofficepaperprod.com']
['Chauncey', 'Motley', 'Affiliated With Travelodge', '63 E Aurora Dr', 'Orlando', 'Orange', 'FL', '32804', '407-413-4842', '407-557-8857', 'chauncey_motley@aol.com', 'http://www.affiliatedwithtravelodge.com']

Μας επιστρέφει λοιπόν μία λίστα από strings. Έτσι με τον εύκολο αυτό τρόπο τυπώσαμε στην οθόνη μας τα περιεχόμενα του αρχείου.

Στον παρακάτω κώδικα βλέπουμε πώς μπορούμε να προσθέσουμε στο παραπάνω αρχείο δύο γραμμές :

#Python Recipe 1
#CSV WRITER
import csv

rows = [["John","Moras","DTEK","Episkopou Amvrosiou 5","Thessaloniki","Macedonia","Thessaloniki","54630","6993054642","2316777065","john@moras.gr","https://dtek.gr"],
["George","Tester","Phoneparts","Agiou Dimitriou 5","Thessaloniki","Macedonia","Thessaloniki","54630","6393574699","2310757666","dont@spam.gr","https://phoneparts.gr"]]

with open('us-500.csv','a+',newline='') as csvFile:
    csvWriter = csv.writer(csvFile)
    for row in rows:
        csvWriter.writerow(row)

Η λογική είναι παρόμοια με την διαδικασία της ανάγνωσης. Αυτό που κάναμε ήταν να δημιουργήσουμε μία λίστα εγγραφών της οποίας η κάθε εγγραφή είναι μία άλλη λίστα η οποία περιλαμβάνει τα πεδία της κάθε γραμμής.

Στη συνέχεια ανοίγουμε με τον τρόπο που δείξαμε το αρχείο μας αλλά αντί για το flag r+ χρησιμοποιήσαμε το a+ με το οποίο δηλώνουμε πώς θέλουμε να προσθέσουμε στο τέλος του αρχείου .

Καλώντας την συνάρτηση writer του csv module μπορούμε να γράψουμε στο αρχείο μας τα περιεχόμενα που δηλώσαμε παραπάνω στην λίστα rows. Έτσι κάνοντας προσπέλαση την λίστα αυτή γράφουμε με τον κώδικα csvWriter.writerow(row) τις νέες μας εγγραφές στο αρχείο μας.

Για εξάσκηση μπορείτε να δοκιμάσετε να αντικαταστήσετε το flag a+ με το w+ και να δείτε τι θα συμβεί.

Στα παραπάνω δύο παραδείγματα είδαμε δύο απλές περιπτώσεις χρήσης. Παρακάτω θα δούμε ένα πιο αντιπροσωπευτικό παράδειγμα από την καθημερινότητα στο οποίο θα κάνουμε χρήση dictionaries προκειμένου το csv μας να είναι πιο περιγραφικό. Συγκεκριμένα θα γράψουμε τις κεφαλίδες του csv και στη συνέχεια τα περιεχόμενα του κάθε dictionary. Ενώ στο τέλος διαβάζουμε με τον τρόπο που δείξαμε παραπάνω τα περιεχόμενα του αρχείου

import csv 
students = [                   {'firstname':'John','lastname':'Moras','Gender':'Male','Address':'Episkopou Amvrosiou 5','City':'Thessaloniki'},                    {'firstname':'George','lastname':'Dimitriou','Gender':'Male','Address':'Aristotelous 4','City':'Veroia'},                    {'firstname':'Dimitris','lastname':'Georgiou','Gender':'Male','Address':'Michael Logiou 5','City':'Naoussa'},                    {'firstname':'Petros','lastname':'Papadopoulos','Gender':'Male','Address':'Pyrrou 6','City':'Athens'}
]
with open('students.csv','w+',newline='') as csvFile:
    csvWriter = csv.writer(csvFile)
    #Παίρνουμε τα keys του 1oυ dictionary τα οποία θα χρησιμοποιήσουμε σαν κεφαλίδες στο csv
    headers = [*students[0]]
    csvWriter = csv.DictWriter(csvFile, fieldnames=headers)
    csvWriter.writeheader()
    for student in students:
        csvWriter.writerow(student)

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

Εδώ αξίζει να προσέξουμε την γραμμή headers = [*students[0]] η οποία μας επιστρέφει τα keys ενός dictionary . Η δυνατότητα αυτή είναι διαθέσιμη σε εκδόσεις PYTHON > 3.4 .Επειδή η μεταβλητή students είναι μία λίστα η οποία περιλαμβάνει dictionaries μου αρκεί η πρώτη εγγραφή. Επίσης προσέχουμε τις δύο παρακάτω γραμμές :

csvWriter = csv.DictWriter(csvFile, fieldnames=headers)    csvWriter.writeheader()

Στην πρώτη γραμμή δηλώνουμε ότι τα ονόματα των πεδίων είναι στη μεταβλητή headers που δηλώσαμε παραπάνω. Η μεταβλητή αυτή χρειάζεται γιατί από την φύση τους τα dictionaries δεν είναι ταξινομημένα και στην δεύτερη γράφουμε το header στο csv αρχείο μας.

Θα κάνουμε τώρα την εφαρμογή μας πιο διαδραστική και θα ζητάμε από τον χρήστη να συμπληρώνει μόνος του τα πεδία τα οποία θέλουν να καταχωρηθούν στο csv αρχείο. Στον παρακάτω κώδικα μπορείτε να δείτε όσα συζητήσαμε παραπάνω με σχόλια όπου χρειάζεται για να τον κάνουν πιο κατανοητό.

import csv

#Διαβάζει τα περιεχόμενα ενός CSV αρχείου
def csvRead(file):
    with open(file,'r+',newline='') as csvFile:
        csvReader = csv.reader(csvFile)    
        for row in csvReader:
            print(str(row))

#Γράφει σε ένα CSV αρχείο τα περιεχόμενα ενός Dictionary
def csvWrite(file,dictionary):
    with open(file,'w+',newline='') as csvFile:
        csvWriter = csv.writer(csvFile)        
        headers = [*dictionary[0]]
        csvWriter = csv.DictWriter(csvFile, fieldnames=headers)
        csvWriter.writeheader()
        for student in dictionary:
            csvWriter.writerow(student)

def test_run():
    #αρχικοποίηση λίστας που θα κρατάει τα dictionaries
    studentList = []
    
    #boolean μεταβλητή με την οποία ανάλογα το περιεχόμενό της θα τερματίζει ή θα συνεχίζει
    flowStatus = True

    while flowStatus:
        
        #Ζητάμε από τον χρήστη να συμπληρώσει τιμές
        firstname = input("Student Firstname: ")
        lastname  = input("Student Lastname: ")
        gender    = input("Gender: ")
        address   = input("Address: ")
        city      = input("City: ")
        
        #Προσθέτουμε το dictionary στην λίστα που δημιουργήσαμε πιο πάνω
        studentList.append({
            "name"      :firstname,
            "lastname"  :lastname,
            "gender"    :gender,
            "address"   :address,
            "city"      :city
        })
        
        #Ρωτάμε τον χρήστη εαν θέλει να καταχωρήσει άλλη εγγραφή
        userflowStatus = input("Do you want to continue y/n? ")
        if userflowStatus != 'y':
            flowStatus = False
        
    #Εγγραφή του χρήστη στο αρχείο
    csvWrite('students.csv',studentList)

    #Ανάγνωση αρχείου
    csvRead('students.csv')
test_run()

Και κάπου εδώ τελείωσε το πρώτο Recipe . Τα αρχεία μπορείτε να τα βρείτε στο github, στον φάκελο csv στο παρακάτω link :
https://github.com/delmoras/python-recipes