web-dev-qa-db-fra.com

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

Je souhaite importer deux types de fichiers CSV, certains utilisent ";" pour délimiteur et autres, utilisez ",". 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
43
rom

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 ";"
7
rom

Le module csv semble recommander l'utilisation de 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-le.

[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 __== '__main__':
    main()

Et nos exemples d'entrées

[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 l'exemple de programme:

[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 convient également de noter la version de python que j'utilise.

[9:20am][wlynch@watermelon /tmp] python -V
Python 2.7.2
47
Bill Lynch

Étant donné un projet qui traite à la fois, (virgule) et | (barre verticale) fichiers CSV délimités, 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 délimité par |, l'exception "Impossible de déterminer le délimiteur" a été renvoyée. Il semblait raisonnable de spéculer que l'heuristique de reniflement pourrait fonctionner mieux si chaque ligne avait le même nombre de délimiteurs (sans compter tout ce qui pourrait être inclus entre guillemets). 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
Andrew Basile

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'])

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

6

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, 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 le nombre , et ; caractères qu'il contient (en ignorant éventuellement ces guillemets intérieurs, si ce qui crée votre .csv files cite les entrées correctement et systématiquement), et devinez que le plus fréquent des deux est le bon délimiteur.

2
twalberg