Plik CSV napisany w Pythonie ma puste wiersze między każdym wierszem

446
import csv

with open('thefile.csv', 'rb') as f:
  data = list(csv.reader(f))
  import collections
  counter = collections.defaultdict(int)

  for row in data:
        counter[row[10]] += 1


with open('/pythonwork/thefile_subset11.csv', 'w') as outfile:
    writer = csv.writer(outfile)
    for row in data:
        if counter[row[10]] >= 504:
           writer.writerow(row)

Ten kod czyta thefile.csv, wprowadza zmiany i zapisuje wyniki thefile_subset1.

Jednak gdy otwieram wynikowy plik csv w programie Microsoft Excel, po każdym rekordzie jest dodatkowy pusty wiersz!

Czy istnieje sposób, aby nie wstawiać dodatkowej pustej linii?

l - '' '' ---------- '' '' '' '
źródło
4
Potwierdź, że dzieje się tak, gdy uruchamiasz ten kod w systemie Windows
John Machin
Prawdopodobnie duplikat programu piszącego CSV
John Y
Zobacz odpowiedź w tym wątku: stackoverflow.com/questions/3348460/…
Febin Mathew

Odpowiedzi:

887

W Pythonie 2 otwórz outfilew trybie 'wb'zamiast 'w'. csv.writerZapisuje \r\ndo pliku bezpośrednio. Jeśli nie otworzysz pliku w trybie binarnym , zapisze, \r\r\nponieważ w systemie Windows tryb tekstowy przetłumaczy każdy \nna \r\n.

W Pythonie 3 wymagana składnia uległa zmianie (patrz łącza do dokumentacji poniżej), więc zamiast tego otwórz outfileza pomocą dodatkowego parametru newline=''(pusty ciąg).

Przykłady:

# Python 2
with open('/pythonwork/thefile_subset11.csv', 'wb') as outfile:
    writer = csv.writer(outfile)

# Python 3
with open('/pythonwork/thefile_subset11.csv', 'w', newline='') as outfile:
    writer = csv.writer(outfile)

Linki do dokumentacji

Mark Tolonen
źródło
1
W każdym razie odpowiedź @Mark Tolonen rozwiązała wiele pytań związanych z dodatkową linią (liniami) dodanymi podczas zapisywania standardowego pliku tekstowego (bez użycia csv).
dlewin
1
Dla kompatybilności pomiędzy 2.6 / 2.7 i 3, można korzystać io.openz newlinesargumentu. Jeśli nadal piszesz w wersji 2.x, i tak wydaje się to lepszym wyborem, ponieważ jest kompatybilny z przyszłością.
jpmc26
@ jpmc26 Zwykle to dobra rada, ale moduł csv nie działa poprawnie io.open. Istnieje unicodecsvmoduł innej firmy dla Python 2.7, który działa lepiej.
Mark Tolonen
Masz pojęcie, dlaczego newline=''sztuczka nie działa w Python3 z StringIO lub TemporaryFile?
fmoo
@fmoo zdefiniuj „nie działa”. Oboje działają tak, jak się spodziewam. StringIObuforuje te same punkty kodowe, które byłyby zakodowane w pliku, i TemporaryFileobsługuje newlineparametr, dzięki czemu można go otworzyć tak jak za pomocą open. Zadaj pytanie za pomocą przykładowego programu, który nie działa.
Mark Tolonen,
65

Otwarcie pliku w trybie binarnym „wb” nie będzie działać w Pythonie 3+. A raczej trzeba będzie przekonwertować dane na dane binarne przed ich zapisaniem. To tylko kłopot.

Zamiast tego powinieneś zachować go w trybie tekstowym, ale zastąpić nowy wiersz jako pusty. Tak jak:

with open('/pythonwork/thefile_subset11.csv', 'w', newline='') as outfile:
David Maddox
źródło
13

Prosta odpowiedź jest taka, że pliki csv powinny zawsze być otwierane w trybie binarnym, zarówno na wejściu, jak i na wyjściu, ponieważ w przeciwnym razie w systemie Windows występują problemy z zakończeniem linii. Konkretnie na wyjściu modułu CSV napisze \r\n(standardowy terminator wiersza CSV), a następnie (w trybie tekstowym) runtime zastępujący \nprzez \r\n(standardowej linii terminatora systemie Windows) daje wynik \r\r\n.

Grzebanie w lineterminatorNIE jest rozwiązaniem.

John Machin
źródło
O czym jest ten „standard” CSV, o którym mówisz?
Dan Breslau
3
@ Dan: Użyłem „standard” jako przymiotnika, a nie rzeczownika, co oznacza „zwykły” lub „pospolity”. Jeśli chcesz uzyskać zbliżenie do (rzeczownikowego) standardu, przeczytaj tools.ietf.org/html/rfc4180
John Machin
1
Chodzi o to (jak sugerujesz), że nie ma standardu. To RFE ma charakter informacyjny. Chociaż \ r \ n może być „standardowy” w systemie Windows, jestem pewien, że aplikacje uniksowe zwykle nie widzą tego w ten sposób.
Dan Breslau
2
@Dan: To prawda - nie ma standardu. Skrypty powinny określać lineterminator [powinien mieć nazwę ROWterminator], który chcą (jeśli nie domyślny) i nadal używać trybu binarnego, jeśli skrypt jest uruchamiany w systemie Windows, w przeciwnym razie „lineterminator” może być upchnięty.
John Machin
8

Uwaga: Wydaje się, że nie jest to preferowane rozwiązanie ze względu na sposób dodawania dodatkowej linii w systemie Windows. Jak stwierdzono w dokumencie python :

Jeśli plik csv jest obiektem pliku, należy go otworzyć flagą „b” na platformach, na których ma to znaczenie.

Windows to jedna z takich platform, na której to robi różnicę. Chociaż zmiana terminatora linii, jak opisano poniżej, mogła rozwiązać problem, problemu można całkowicie uniknąć, otwierając plik w trybie binarnym. Można powiedzieć, że to rozwiązanie jest bardziej „eleganckie”. „Błąkanie się” za pomocą terminatora linii prawdopodobnie spowodowałoby w tym przypadku niemożliwy do przeniesienia kod między systemami, w którym otwarcie pliku w trybie binarnym w systemie uniksowym nie daje żadnego efektu. to znaczy. powoduje kod kompatybilny z wieloma systemami.

Z Python Docs :

W systemie Windows „b” dołączony do trybu otwiera plik w trybie binarnym, więc istnieją również tryby takie jak „rb”, „wb” i „r + b”. Python w systemie Windows rozróżnia pliki tekstowe i binarne; znaki końca wiersza w plikach tekstowych są automatycznie nieznacznie zmieniane podczas odczytu lub zapisu danych. Ta zakulisowa modyfikacja danych pliku jest odpowiednia dla plików tekstowych ASCII, ale spowoduje uszkodzenie takich danych binarnych w plikach JPEG lub EXE. Podczas odczytu i zapisu takich plików należy bardzo uważać na tryb binarny. W systemie Unix dodanie „b” do trybu nie boli, więc możesz używać go niezależnie od platformy dla wszystkich plików binarnych.

Oryginał :

Jako część opcjonalnych parametrów dla csv.writer, jeśli otrzymujesz dodatkowe puste linie, być może będziesz musiał zmienić lineterminator (informacje tutaj ). Przykład poniżej dostosowany ze strony csv docs strony python . Zmień to z „\ n” na cokolwiek powinno być. Ponieważ jest to po prostu kłótnia w ciemności na problem, to może, ale nie musi działać, ale to moje najlepsze przypuszczenie.

>>> import csv
>>> spamWriter = csv.writer(open('eggs.csv', 'w'), lineterminator='\n')
>>> spamWriter.writerow(['Spam'] * 5 + ['Baked Beans'])
>>> spamWriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])
Derek Litz
źródło
Już miałem o tym pisać - lineterminator = '\ n' pracował dla mnie w prostym teście.
Dan Breslau
czy mogę to zrobić? z open ('/ pythonwork / thefile_subset11.csv', 'w'), lineterminator = '\ n' jako outfile:
l
1
@I__: Naprawdę powinieneś zacząć czytać dokumenty Pythona. Derek dał ci link: docs.python.org/library/csv.html
Dan Breslau
5

Piszę tę odpowiedź wrt do Pythona 3, ponieważ początkowo mam ten sam problem.

Miałem pobrać dane z arduino PySeriali zapisać je w pliku .csv. Każde czytanie w moim przypadku kończyło się '\r\n', więc nowa linia zawsze oddzielała każdą linię.

W moim przypadku newline=''opcja nie działała. Ponieważ pokazał błąd:

with open('op.csv', 'a',newline=' ') as csv_file:

ValueError: illegal newline value: ''

Wydawało się więc, że nie akceptują tutaj pominięcia nowej linii.

Widząc tylko jedną z odpowiedzi tutaj, wspomniałem terminator linii w obiekcie piszącym, np .:

writer = csv.writer(csv_file, delimiter=' ',lineterminator='\r')

i to zadziałało dla mnie, pomijając dodatkowe nowe linie.

Debanjan Dey
źródło
2
To jest niepoprawne. with open('my_file.csv', 'a',newline='') as csvfile: działa absolutnie dobrze. Problem z twoją odpowiedzią polega na tym, że piszesz tutaj ' 'zamiast''
Nasrin,
2
with open(destPath+'\\'+csvXML, 'a+') as csvFile:
    writer = csv.writer(csvFile, delimiter=';', lineterminator='\r')
    writer.writerows(xmlList)

„Lineterminator = '\ r” ”pozwala przejść do następnego wiersza bez pustego wiersza między dwoma.

SheRa
źródło
1

Pożyczanie od tej odpowiedzi , wydaje się, że najczystszym rozwiązaniem jest użycie io.TextIOWrapper. Udało mi się rozwiązać ten problem dla siebie w następujący sposób:

from io import TextIOWrapper

...

with open(filename, 'wb') as csvfile, TextIOWrapper(csvfile, encoding='utf-8', newline='') as wrapper:
    csvwriter = csv.writer(wrapper)
    for data_row in data:
        csvwriter.writerow(data_row)

Powyższa odpowiedź nie jest kompatybilna z Pythonem 2. Aby mieć kompatybilność, przypuszczam, że wystarczy po prostu zawrzeć całą logikę pisania w ifbloku:

if sys.version_info < (3,):
    # Python 2 way of handling CSVs
else:
    # The above logic
phantom-99w
źródło
0

Użyj metody zdefiniowanej poniżej, aby zapisać dane do pliku CSV.

open('outputFile.csv', 'a',newline='')

Wystarczy dodać dodatkowy newline=''parametr w openmetodzie:

def writePhoneSpecsToCSV():
    rowData=["field1", "field2"]
    with open('outputFile.csv', 'a',newline='') as csv_file:
        writer = csv.writer(csv_file)
        writer.writerow(rowData)

Spowoduje to zapisanie wierszy CSV bez tworzenia dodatkowych wierszy!

Febin Mathew
źródło
-1

Korzystając z Python 3, można uniknąć pustych linii za pomocą modułu kodeków . Jak stwierdzono w dokumentacji, pliki są otwierane w trybie binarnym, więc żadna zmiana kwarg nowej linii nie jest konieczna. Ostatnio miałem ten sam problem i to działało dla mnie:

with codecs.open( csv_file,  mode='w', encoding='utf-8') as out_csv:
     csv_out_file = csv.DictWriter(out_csv)
JBa
źródło