48. Werken met CSV bestanden

Auteur Richard Philips
Aanmaak 10 sept 2010
Oud BVV nr 2249

48.1. Abstract

Dit document beschrijft hoe in de Brocade software het best wordt omgesprongen met CSV bestanden.

48.2. Algemeen

Bij het werken met CSV bestanden is het specificeren van de delimiter nogal omslachtig. Daarom de volgende richtlijnen:

  • Voorzie steeds een techniek om de delimiter expliciet te kunnen specificeren. Bij toolcat applicaties kan dit door de modifier delimiter. Dit is echter geen standaard modifier en elke toolcatapplicatie moet deze modifier zelf definieren en verwerken.

  • Wordt de delimiter dan toch niet gespecificeerd door de gebruiker, werk dan als volgt:

    • Bij het schrijven: gebruik dan steeds de registry waarde lc-csvdelimiter tenzij er goede redenen zijn (bijvoorbeeld externe specificaties) om daar anders over te beslissen. De standaard software is zelfs zo, dat indien de delimiter niet wordt vermeld, er steeds de lc-csvdelimiter wordt gebruikt.
    • Bij het lezen: gebruik dan fs.getcsvdelimiter(filename)
    • Als je gebruik maakt van de onderstaande software, dan wordt dit voor jou gedaan: in elke subroutine kan met delimiter specificeren.

    Ook het gebruik van een header lijn, vereist wat meer uitleg:

    Bij het schrijven:
    • Indien de parameter fieldnames niet werd gespecificeerd, dan wordt een header enkel geschreven indien dit expliciet gebeurt.
    • Indien de parameter fieldnames werd gespecificeerd, dan wordt een header altijd geschreven.
    Bij het lezen:
    • Een header wordt enkel doorgegeven aan de verwerkende software (generator, subroutine) indien de fieldnames niet werden gespecificieerd en de objecten Python lijsten of Mumps numerieke arrays zijn.

De software maakt gebruik van de beste verwerkende technieken beschikbaar. Dit betekent dat de verwerkingstechnieken asymmetrisch zijn:

Voor Python
gebaseerd op iterators
Voor Mumps
gebaseerd op subroutines

48.3. Het verwerken van bestaande CSV bestanden in Python

/core/python/fs.py bevat - naargelang de bestaande structuren - drie verschillende methodes:

fs.yieldcsvrows
Dit is een generator die de data aanreikt in de vorm van lists.
fs.yieldcsvdicts
Dit is een generator die de data aanreikt in de vorm van dicts.
fs.yieldcsvrecords
Dit is een generator die de data aanreikt in de vorm van named tuples.

De aangewezen methode is yieldcsv uit /core/python/fs.py

Het volgende voorbeeld toont de aangewezen technieken:

Voorbeeld:

from anet.core import fs

fs.store("data.csv","A;B;C\r\n1;2;3\r\n") # initialiseert de test file

# Produces dicts:
for d in fs.yieldcsvdicts("data.csv",fieldnames=["a", "b", "c"]):
    print d  # prints:
             #    {'a': 'A', 'c': 'C', 'b': 'B'}
             #    {'a': '1', 'c': '3', 'b': '2'}

# Produces lists:
for d in fs.yieldcsvrows("data.csv"):
    print d  # prints:
             #    ['A', 'B', 'C']
             #    ['1', '2', '3']

# Produces named tuples: (preferred !) only for python >= 2.6
for d in fs.yieldcsvrecords("data.csv", recordtype="DataRecord", fieldnames=["a", "b", "c"]):
    print d  # prints:
             #    DataRecord(a='A', b='B', c='C', extra_='')
             #    DataRecord(a='1', b='2', c='3', extra_='')

Tip

Vergeet de kracht niet van python iterators: door het combineren van diverse iterators kan men complexe problemen oplossen.

Het volgende voorbeeld toont hoe men een collectie van gelijkaardige bestanden kan verwerken. Stel dat alle bestanden van de gedaante *.csv in een gegeven folder dient te verwerken:

Voorbeeld:

import itertools
from anet.core import fs

for row in itertools.chain(*(fs.yieldcsvrows(f) for f in sorted(fs.yieldfiles(startdir="/library/tmp/csv", patterns="*.csv")))):
    print row

48.4. Het schrijven van CSV bestanden met Python

/core/python/fs.py bevat - naargelang de bestaande structuren - drie verschillende methodes:

fs.storecsvrows
De data wordt aangeleverd met behulp van een ‘iterable’ van lists.
fs.storecsvdicts
De data wordt aangeleverd met behulp van een ‘iterable’ van dicts. De sleutels komen overeen met de veldnamen van de CSV.
fs.storecsvrecords
De data wordt aangeleverd met behulp van een ‘iterable’ van namedtuples. De sleutels komen overeen met de veldnamen van de CSV.

Het volgende voorbeeld toont de aangewezen technieken:

Voorbeeld:

from anet.core import fs

def myIterableRows():
    yield ["a1", "b1", "c1"]
    yield ["a2", "b2", "c2"]
    yield ["a3", "b3", "c3"]
    yield ["a4", "b4", "c4"]

def myIterableDicts():
    yield {'A': "a1", 'B': "b1", 'C': "c1"}
    yield {'A': "a2", 'B': "b2", 'C': "c2"}
    yield {'A': "a3", 'B': "b3", 'C': "c3"}
    yield {'A': "a4", 'B': "b4", 'C': "c4"}

def myIterableRecords():
    from collections import namedtuple
    Record = namedtuple('MyRecord', 'A B C')
    yield Record(A="a1", B="b1", C="c1")
    yield Record(A="a2", B="b2", C="c2")
    yield Record(A="a3", B="b3", C="c3")
    yield Record(A="a4", B="b4", C="c4")

# Store To CSV file: the 3 files contain the same data

fs.storecsvrows("rows.csv", iterable=myIterableRows(), fieldnames=["A", "B", "C"])
fs.storecsvdicts("dicts.csv", iterable=myIterableDicts(), fieldnames=["A", "B", "C"])
fs.storecsvrecords("records.csv", iterable=myIterableRecords(), fieldnames=["A", "B", "C"])

48.5. Het verwerken van bestaande CSV bestanden in Mumps

/universe/stdlib/csv.d bevat - naargelang de gewenste structuren - twee verschillende methodes:

m4_readcsvrows
De CSV data komt ter beschikking in een M array met numerieke subscripts en deze worden na elkaar verwerkt in een gegeven subroutine.
m4_readcsvdicts
De CSV data komt ter beschikking in een M array met veldnamen en deze worden na elkaar verwerkt in een gegeven subroutine.

Het volgende voorbeeld toont de aangewezen technieken:

Voorbeeld:

"""
About: Demonstreer het lezen van een CSV bestand
"""

def %Entry:
  n x,y,z,error,crlf
  ; maak een testbestand aan
  s crlf=m4_CRLF
  s RAlines(1)="a;b;c;d"
  s RAlines(2)="a1;b1;c1;d1"
  s RAlines(3)="a2;b2;c2;d2"
  s RAlines(4)="a3;b3;c3;d3"
  s RAlines(5)="a4;b4;c4;d4"
  m4_writeFile(error, "/library/tmp/test.csv", "RAlines", crlf)

  ; lees het testbestand rij per rij en print de 2 kolom
  m4_readcsvrows("/library/tmp/test.csv", "Row")

  ; lees het testbestand en print de c-kolom
  m4_readcsvdicts("/library/tmp/test.csv", "Dict")
  q

def Row:
  w "kolom 2:",RAcsv(2),!
  q

def Dict:
  w "kolom c:",RAcsv("c"),!
  q

48.6. Het schrijven van CSV bestanden van uit Mumps

/universe/stdlib/csv.d bevat - naargelang de gewenste structuren - twee verschillende methodes:

m4_storecsvrows
De CSV data komt ter beschikking in een M array met numerieke subscripts en deze worden gegnereerd in een gegeven subroutine. Is deze M array leeg, dan is het genereer proces voltooid.
m4_storecsvdicts
De CSV data komt ter beschikking in een M array met veldnamen en deze worden gegnereerd in een gegeven subroutine. Is deze M array leeg, dan is het genereer proces voltooid.

Het volgende voorbeeld toont de aangewezen technieken:

Voorbeeld:

"""
About: Demo van het schrijven van een CSV bestand
"""

def %Entry:
  n x,y,z
  ; schrijf de rijen uit gegeneerd door MyRows
  m4_storecsvrows("/library/tmp/test.csv", "MyRows")

  ; schrijf de arrays uit gegeneerd door MyDicts
  m4_storecsvdicts("/library/tmp/test.csv", "MyDicts", "a^b^c^d")
  q

def MyRows:
  k RAcsv
  q:RDcount>5
  s RAcsv("1")="a"_RDcount,RAcsv("2")="b"_RDcount,RAcsv("3")="c"_RDcount,RAcsv("4")="d"_RDcount
  q

def MyDicts:
  k RAcsv
  q:RDcount>5
  s RAcsv("a")="a"_RDcount,RAcsv("b")="b"_RDcount,RAcsv("c")="c"_RDcount,RAcsv("d")="d"_RDcount
  q