2011-03-17 10 views
9

Przesyłam plik csv/tsv z formularza w GAE i próbuję przeanalizować plik za pomocą modułu pythona csv.Prześlij i przeanalizuj plik CSV za pomocą "uniwersalnej linii nowej" w pythonie w Google App Engine

Podobnie jak w przypadku opisu here, przesłane pliki w GAE są ciągami.
więc traktować mnie plik String obiekt plikopodobny:

file = self.request.get('catalog') 
catalog = csv.reader(StringIO.StringIO(file),dialect=csv.excel_tab) 

Ale nowe linie w moich plikach niekoniecznie „\ n” (dzięki excel ..), a generowany błąd:
Błąd: znak nowego wiersza widziany w niecytowanym polu - czy musisz otworzyć plik w trybie uniwersalnej-nowej linii?

Czy ktoś wie, jak używać StringIO.StringIO do traktowania ciągów takich jak pliki otwarte w uniwersalnej linii?

+0

Zgodnie z docs Pythona, tryb domyślny StringIO jest uniwersalny znak nowej linii. Coś dziwnego może się dziać w twoim pliku danych. – Calvin

+0

@Calvin _ "Zgodnie z dokumentami Pythona, domyślnym trybem StringIO jest uniwersalny znak nowej linii" _ Nie znalazłem miejsca, w którym mówią o tym doktorzy, mogliby go pokazać? – eyquem

+0

@eyquem Minęło 2,5 roku, więc dokumenty mogły się zmienić, ale http://docs.python.org/3.3/library/io.html?highlight=stringio#io.StringIO mówi: "Argument nowej linii działa tak, jak w TextIOWrapper "i TextIOWrapper mówi" jeśli nowa linia jest Brak, włączony jest tryb uniwersalnych linii nowych. " Ale wtedy StringIO może zaprzeczyć temu, mówiąc "Domyślnie nie wykonujemy tłumaczenia nowej linii". – Calvin

Odpowiedz

5

Jak o:

file = self.request.get('catalog') 
file = '\n'.join(file.splitlines()) 
catalog = csv.reader(StringIO.StringIO(file),dialect=csv.excel_tab) 

lub jak wskazano w uwagach, csv.reader() obsługuje wejście z listy, więc:

file = self.request.get('catalog') 
catalog = csv.reader(file.splitlines(),dialect=csv.excel_tab) 

lub jeśli w przyszłości request.get podpór czytać tryby:

file = self.request.get('catalog', 'rU') 
catalog = csv.reader(StringIO.StringIO(file),dialect=csv.excel_tab) 
+0

Używam faktycznie .splitlines(), ale pracuję na bardzo dużych plikach i nie jest to zbyt szybkie. request.get() nie obsługuje trybów odczytu. – greg

+1

@greg: Wtedy utkniesz. Możesz go emulować wykonując .replace ('\ r \ n', '\ n'), jeśli jest to jedyne zakończenie linii, które musisz zmienić. – theheadofabroom

+4

@greg Przyjąłbym prędkość hit i użyj 'catalog = csv.reader (file.splitlines(), dialect = csv.excel_tab)'. (csv reader może zaakceptować listę napisów) – Calvin

4

Rozwiązanie opisane here powinno działać. Przez zdefiniowanie klasy iteratora w następujący sposób, która ładuje pojedynczy blok o wielkości 1 MB, dzieli linie za pomocą .splitlines(), a następnie przesyła wiersze do czytnika CSV pojedynczo, nowe linie mogą być obsługiwane bez konieczności ładowania całego pliku do pamięci.

class BlobIterator: 
    """Because the python csv module doesn't like strange newline chars and 
    the google blob reader cannot be told to open in universal mode, then 
    we need to read blocks of the blob and 'fix' the newlines as we go""" 

    def __init__(self, blob_reader): 
     self.blob_reader = blob_reader 
     self.last_line = "" 
     self.line_num = 0 
     self.lines = [] 
     self.buffer = None 

    def __iter__(self): 
     return self 

    def next(self): 
     if not self.buffer or len(self.lines) == self.line_num + 1: 
      self.buffer = self.blob_reader.read(1048576) # 1MB buffer 
      self.lines = self.buffer.splitlines() 
      self.line_num = 0 

      # Handle special case where our block just happens to end on a new line 
      if self.buffer[-1:] == "\n" or self.buffer[-1:] == "\r": 
       self.lines.append("") 

     if not self.buffer: 
      raise StopIteration 

     if self.line_num == 0 and len(self.last_line) > 0: 
      result = self.last_line + self.lines[self.line_num] + "\n" 
     else: 
      result = self.lines[self.line_num] + "\n" 

     self.last_line = self.lines[self.line_num + 1] 
     self.line_num += 1 

     return result 

Następnie nazwać to tak:

blob_reader = blobstore.BlobReader(blob_key) 
blob_iterator = BlobIterator(blob_reader) 
reader = csv.reader(blob_iterator) 
+0

pracował jak urok. Dzięki za dużo –

+1

W plikach csv, możesz mieć znaki nowej linii w pojedynczej "komórce", jeśli komórka jest cytowana. Technika splitlines() wpadłaby w ten scenariusz. – Troy