Puis-je importer un fichier CSV et déduire automatiquement le délimiteur?

Je veux importer deux types de fichiers CSV, certains utilisent"; " pour délimiteur et d'autres utilisent ",". Jusqu'à présent, j'ai basculé entre les deux lignes suivantes:

reader=csv.reader(f,delimiter=';')

Ou

reader=csv.reader(f,delimiter=',')

Est-il possible de ne pas spécifier le délimiteur et de laisser le programme vérifier le bon délimiteur?

Les solutions ci-dessous (Blender et sharth) semblent bien fonctionner pour les fichiers séparés par des virgules (générés avec Libroffice) mais pas pour les fichiers séparés par des points-virgules (générés avec MS Office). Voici les premières lignes d'un fichier séparé par des points-virgules:

ReleveAnnee;ReleveMois;NoOrdre;TitreRMC;AdopCSRegleVote;AdopCSAbs;AdoptCSContre;NoCELEX;ProposAnnee;ProposChrono;ProposOrigine;NoUniqueAnnee;NoUniqueType;NoUniqueChrono;PropoSplittee;Suite2LecturePE;Council PATH;Notes
1999;1;1;1999/83/EC: Council Decision of 18 January 1999 authorising the Kingdom of Denmark to apply or to continue to apply reductions in, or exemptions from, excise duties on certain mineral oils used for specific purposes, in accordance with the procedure provided for in Article 8(4) of Directive 92/81/EEC;U;;;31999D0083;1998;577;COM;NULL;CS;NULL;;;;Propos* are missing on Celex document
1999;1;2;1999/81/EC: Council Decision of 18 January 1999 authorising the Kingdom of Spain to apply a measure derogating from Articles 2 and 28a(1) of the Sixth Directive (77/388/EEC) on the harmonisation of the laws of the Member States relating to turnover taxes;U;;;31999D0081;1998;184;COM;NULL;CS;NULL;;;;Propos* are missing on Celex document
41
demandé sur Martin Thoma 2013-05-01 06:55:16

5 réponses

Le csv module semble vous recommandons d'utiliser le csv sniffer pour ce problème.

, Ils donnent l'exemple suivant, que j'ai adapté à votre cas.

with open('example.csv', 'rb') as csvfile:  # python 3: 'r',newline=""
    dialect = csv.Sniffer().sniff(csvfile.read(1024), delimiters=";,")
    csvfile.seek(0)
    reader = csv.reader(csvfile, dialect)
    # ... process CSV file contents here ...

Essayons.

[9:13am][wlynch@watermelon /tmp] cat example 
#!/usr/bin/env python
import csv

def parse(filename):
    with open(filename, 'rb') as csvfile:
        dialect = csv.Sniffer().sniff(csvfile.read(), delimiters=';,')
        csvfile.seek(0)
        reader = csv.reader(csvfile, dialect)

        for line in reader:
            print line

def main():
    print 'Comma Version:'
    parse('comma_separated.csv')

    print
    print 'Semicolon Version:'
    parse('semicolon_separated.csv')

    print
    print 'An example from the question (kingdom.csv)'
    parse('kingdom.csv')

if __name__ == '__main__':
    main()

Et nos entrées d'échantillon

[9:13am][wlynch@watermelon /tmp] cat comma_separated.csv 
test,box,foo
round,the,bend

[9:13am][wlynch@watermelon /tmp] cat semicolon_separated.csv 
round;the;bend
who;are;you

[9:22am][wlynch@watermelon /tmp] cat kingdom.csv 
ReleveAnnee;ReleveMois;NoOrdre;TitreRMC;AdopCSRegleVote;AdopCSAbs;AdoptCSContre;NoCELEX;ProposAnnee;ProposChrono;ProposOrigine;NoUniqueAnnee;NoUniqueType;NoUniqueChrono;PropoSplittee;Suite2LecturePE;Council PATH;Notes
1999;1;1;1999/83/EC: Council Decision of 18 January 1999 authorising the Kingdom of Denmark to apply or to continue to apply reductions in, or exemptions from, excise duties on certain mineral oils used for specific purposes, in accordance with the procedure provided for in Article 8(4) of Directive 92/81/EEC;U;;;31999D0083;1998;577;COM;NULL;CS;NULL;;;;Propos* are missing on Celex document
1999;1;2;1999/81/EC: Council Decision of 18 January 1999 authorising the Kingdom of Spain to apply a measure derogating from Articles 2 and 28a(1) of the Sixth Directive (77/388/EEC) on the harmonisation of the laws of the Member States relating to turnover taxes;U;;;31999D0081;1998;184;COM;NULL;CS;NULL;;;;Propos* are missing on Celex document

Et si nous exécutons le programme exemple:

[9:14am][wlynch@watermelon /tmp] ./example 
Comma Version:
['test', 'box', 'foo']
['round', 'the', 'bend']

Semicolon Version:
['round', 'the', 'bend']
['who', 'are', 'you']

An example from the question (kingdom.csv)
['ReleveAnnee', 'ReleveMois', 'NoOrdre', 'TitreRMC', 'AdopCSRegleVote', 'AdopCSAbs', 'AdoptCSContre', 'NoCELEX', 'ProposAnnee', 'ProposChrono', 'ProposOrigine', 'NoUniqueAnnee', 'NoUniqueType', 'NoUniqueChrono', 'PropoSplittee', 'Suite2LecturePE', 'Council PATH', 'Notes']
['1999', '1', '1', '1999/83/EC: Council Decision of 18 January 1999 authorising the Kingdom of Denmark to apply or to continue to apply reductions in, or exemptions from, excise duties on certain mineral oils used for specific purposes, in accordance with the procedure provided for in Article 8(4) of Directive 92/81/EEC', 'U', '', '', '31999D0083', '1998', '577', 'COM', 'NULL', 'CS', 'NULL', '', '', '', 'Propos* are missing on Celex document']
['1999', '1', '2', '1999/81/EC: Council Decision of 18 January 1999 authorising the Kingdom of Spain to apply a measure derogating from Articles 2 and 28a(1) of the Sixth Directive (77/388/EEC) on the harmonisation of the laws of the Member States relating to turnover taxes', 'U', '', '', '31999D0081', '1998', '184', 'COM', 'NULL', 'CS', 'NULL', '', '', '', 'Propos* are missing on Celex document']

Il est également probablement intéressant de noter quelle version de python j'utilise.

[9:20am][wlynch@watermelon /tmp] python -V
Python 2.7.2
43
répondu Bill Lynch 2018-01-01 10:13:14

Étant donné un projet qui traite à la fois des fichiers CSV délimités (virgule) et | (barre verticale), qui sont bien formés, j'ai essayé ce qui suit (comme indiqué à https://docs.python.org/2/library/csv.html#csv.Sniffer):

dialect = csv.Sniffer().sniff(csvfile.read(1024), delimiters=',|')

Cependant, sur un fichier / - delimited, l'exception "Could not determine delimiter" a été renvoyée. Il semblait raisonnable de spéculer que l'heuristique sniff pourrait fonctionner mieux si chaque ligne a le même nombre de délimiteurs (sans compter ce qui pourrait être inclus dans citation). Donc, au lieu de lire les 1024 premiers octets du fichier, j'ai essayé de lire les deux premières lignes dans leur intégralité:

temp_lines = csvfile.readline() + '\n' + csvfile.readline()
dialect = csv.Sniffer().sniff(temp_lines, delimiters=',|')

Jusqu'à présent, cela fonctionne bien pour moi.

7
répondu Andrew Basile 2016-01-10 23:13:21

Pour résoudre le problème, j'ai créé une fonction qui lit la première ligne d'un fichier (en-tête) et détecte le délimiteur.

def detectDelimiter(csvFile):
    with open(csvFile, 'r') as myCsvfile:
        header=myCsvfile.readline()
        if header.find(";")!=-1:
            return ";"
        if header.find(",")!=-1:
            return ","
    #default delimiter (MS Office export)
    return ";"
6
répondu rom 2015-04-29 14:36:11

Et si vous utilisez DictReader vous pouvez le faire:

#!/usr/bin/env python
import csv

def parse(filename):
    with open(filename, 'rb') as csvfile:
        dialect = csv.Sniffer().sniff(csvfile.read(), delimiters=';,')
        csvfile.seek(0)
        reader = csv.DictReader(csvfile, dialect=dialect)

        for line in reader:
            print(line['ReleveAnnee'])

J'ai utilisé ceci avec Python 3.5 et cela a fonctionné de cette façon.

6
répondu Vladir Parrado Cruz 2016-05-19 11:56:29

Je ne pense pas qu'il puisse y avoir une solution parfaitement générale à cela (l'une des raisons pour lesquelles je pourrais utiliser , comme délimiteur est que certains de mes champs de données doivent pouvoir inclure ;...). Une heuristique simple pour décider pourrait être de simplement lire la première ligne( ou plus), compter combien de caractères , et ; Il contient (en ignorant éventuellement les guillemets à l'intérieur, si quoi que ce soit crée vos .csv fichiers cite les entrées correctement et uniformément), et devinez que le plus fréquent des deux est le droit délimiteur.

2
répondu twalberg 2013-05-01 15:08:46