2013-05-01 13 views
40

Chcę zaimportować dwa rodzaje plików CSV, a niektóre użyć ";" dla ogranicznika i innych użyj ",". Do tej pory zostały przełączanie kolejnych dwóch liniach:Czy mogę zaimportować plik CSV i automatycznie wyprowadzić separator?

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

lub

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

Czy to możliwe, aby nie określić separator i pozwolić Sprawdzić program dla prawego ogranicznika?

Poniższe rozwiązania (Blender i sharth) wydają się dobrze działać w przypadku plików rozdzielanych przecinkami (generowanych przy użyciu Libroffice), ale nie w przypadku plików rozdzielanych średnikami (generowanych za pomocą MS Office). Oto pierwsze linie z jednego pliku oddzielone średnikami:

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 
+0

Witaj, bardziej ogólna dyskusja (nie w python) jest również w https://stackoverflow.com/questions/2789695/how-to-programmatically-guess-whether-a-csv-file-is-comma-or- oddzielone średnikami – Lorenzo

Odpowiedz

6

Aby rozwiązać problem, utworzyłem funkcję, która odczytuje pierwszy wiersz pliku (nagłówek) i wykrywa separator.

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 ";" 
+5

Twoja funkcja nie działa, jeśli ogranicznik jest częścią wartości, nawet jeśli jest zeskakiwany lub cytowany. Na przykład wiersz "Hi Peter", "How are you?", "Bye John!" "Zwróci'; 'jako ogranicznik, co jest błędne. – tashuhka

42

Moduł csv wydaje polecić pomocą csv sniffer tego problemu.

Podają następujący przykład, który dostosowałem do twojego przypadku.

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 ... 

Spróbujmy ją.

[9:13am][[email protected] /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() 

A nasze wejść Przykładowe

[9:13am][[email protected] /tmp] cat comma_separated.csv 
test,box,foo 
round,the,bend 

[9:13am][[email protected] /tmp] cat semicolon_separated.csv 
round;the;bend 
who;are;you 

[9:22am][[email protected] /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 

A jeśli wykonujemy przykładowy program:

[9:14am][[email protected] /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'] 

Jest to także chyba warto zauważyć, jaka wersja Pythona używam.

[9:20am][[email protected] /tmp] python -V 
Python 2.7.2 
+0

Działa to w przypadku pliku rozdzielanego przecinkami, ale plik rozdzielany średnikami nie może być odczytany poprawnie (nie można określić separatora). Zobacz moją edycję powyżej ... – rom

+0

Wygląda na to, że działa dla mnie. Rozwiążę odpowiedź. –

+0

Dołączyłem przykład z oddzielonym przecinkiem, oddzielonym średnikiem i przykładowym plikiem, który sugerujesz w pytaniu. –

2

Nie sądzę, nie może być idealnie ogólne rozwiązanie (to jeden z powodów, że mogę używać , jako ogranicznik jest to, że niektóre z moich pól danych muszą być w stanie uwzględnić ; ...). Prostą heurystyką do podjęcia decyzji może być po prostu przeczytanie pierwszej linii (lub więcej), policzyć, ile znaków zawiera ona (prawdopodobnie ignorując te cudzysłowy, jeśli to, co tworzy twoje pliki .csv, cytuje wpisy poprawnie i konsekwentnie) i zgadnij, że częstsze z nich to właściwy ogranicznik.

7

Biorąc pod uwagę projekt, który dotyczy zarówno (przecinek) i | (Pionowa kreska) rozdzielany plików CSV, które są dobrze uformowane, próbowałem następujące (jak podano w https://docs.python.org/2/library/csv.html#csv.Sniffer):

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

Jednak na | -delimited pliku, wyjątek „Nie można określić separator” został zwrócony . Wydawało się rozsądne spekulować, że funkcja wykrywania węchu może działać najlepiej, jeśli każda linia ma taką samą liczbę ograniczników (nie licząc tego, co może być zawarte w cudzysłowach).Tak więc, zamiast czytania pierwszych 1024 bajtów pliku, próbowałem czytać pierwsze dwa wiersze w całości:

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

tak daleko, to działa dobrze dla mnie.

+2

To było dla mnie bardzo pomocne! Miałem problemy z danymi, w których jedną z "sztywnych" wartości były cyfry z przecinkami, więc nie udało się. Ograniczenie tego do pierwszych 2 linii naprawdę pomogło. – mauve

+0

Świetnie, pracował dla mnie z moimi | -separowanymi plikami "csv". Dzięki :) – EisenHeim

6

A jeśli używasz DictReader można to zrobić:

#!/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']) 

użyłem tego z Python 3.5 i to działało w ten sposób.

+1

Użyłem go w python 2.7 – alvaro562003

Powiązane problemy