2017-02-18 4 views
3

Ich habe Daten aus CSV gelesen, wenn es eine große CSV-Datei ist, um dieses Timeout zu vermeiden (Rack 12 sec Timeout) habe ich nur 25 Zeilen von CSV nach 25 Zeilen gelesen es zurück und wieder eine Anfrage machen, so wird dies fortgesetzt, bis alle Zeilen gelesen werden.Lesen von Daten aus CSV-Datei mit Foreach-Funktion

def read_csv(offset) 
    r_count = 1 
    CSV.foreach(file.tempfile, options) do |row| 
    if r_count > offset.to_i 
    #process 
    end 
    r_count += 1 
end 

Aber hier wird es ein neues Problem zu schaffen, sagen ersten 25 Zeilen lesen dann, wenn die nächste Anforderung ist 25 Offset kommt, dass die Zeit es bis zu ersten 25 Zeilen lesen, dann wird es von 26 zu lesen beginnen und Verfahren zu tun , also wie kann ich diese Zeilen überspringen, die bereits lesen ?, versuchte ich diese wenn nächste Iteration zu überspringen, aber das scheitert, oder gibt es eine andere effiziente Möglichkeit, dies zu tun?

+1

Wie habe ich diese Upload-Funktion für meine Anwendung – django

+1

Haben Sie in Betracht gezogen, die CVS in einem Hintergrundjob zu verarbeiten? – spickermann

+1

Nein, weil ich die endgültige Zählung zeigen muss, wie viele Elemente auf den Benutzer hochgeladen werden, auch wenn es ein Problem mit der CSV gibt, das ich auch dem Benutzer – django

Antwort

0

-Code

def read_csv(fileName) 
    lines = (`wc -l #{fileName}`).to_i + 1 
    lines_processed = 0 
    open(fileName) do |csv| 
    csv.each_line do |line| 
     #process 
     lines_processed += 1 
    end 
    end 
end 

rein Rubin - Langsamer

def read_csv(fileName) 
    lines = open("sample.csv").count 
    lines_processed = 0 
    open(fileName) do |csv| 
    csv.each_line do |line| 
     #process 
     lines_processed += 1 
    end 
    end 
end 

Benchmarks

lief ich einen neuen Maßstab ursprüngliche Methode Vergleich zur Verfügung gestellt und meine eigenen. Ich habe auch die Informationen zur Testdatei hinzugefügt.

"File Information" 
Lines: 1172319 
Size: 126M 

"django's original method" 
Time: 18.58 secs 
Memory: 0.45 MB 

"OneNeptune's method" 
Time: 0.58 secs 
Memory: 2.18 MB 

"Pure Ruby method" 
Time: 0.96 
Memory: 2.06 MB 

Erklärung

HINWEIS: habe ich ein reines Ruby-Methode, da wc verwendet, ist eine Art Betrug, und nicht tragbar. In den meisten Fällen ist es wichtig, reine Sprachlösungen zu verwenden.

Sie können diese Methode verwenden, um eine sehr große CSV-Datei zu verarbeiten.

~ 2MB Speicher Ich denke, ist ziemlich optimal in Anbetracht der Dateigröße, es ist ein bisschen eine Zunahme der Speicherauslastung, aber die Zeitersparnis scheint ein fairer Handel zu sein, und dies wird Timeouts verhindern.

Ich habe die Methode geändert, um einen Dateinamen zu nehmen, aber das war nur, weil ich viele verschiedene CSV-Dateien getestet habe, um sicherzustellen, dass sie alle richtig funktionierten. Sie können dies entfernen, wenn Sie möchten, aber es wird wahrscheinlich hilfreich sein.

Ich habe auch das Konzept eines Offsets entfernt, da Sie ursprünglich angegeben haben, dass Sie versuchen, das Parsing selbst zu optimieren, aber das ist nicht mehr notwendig.

Auch ich verfolge, wie viele Zeilen in der Datei sind und wie viele verarbeitet wurden, seit Sie diese Informationen verwenden mussten. Beachten Sie, dass Zeilen nur auf Unix-basierten Systemen funktionieren, und es ist ein Trick, um zu vermeiden, dass die gesamte Datei in den Speicher geladen wird, sie zählt die neuen Zeilen und ich füge 1 hinzu, um die letzte Zeile zu berücksichtigen. Wenn Sie die Header nicht als Zeile zählen, können Sie die +1 entfernen und die Zeilen in "Zeilen" ändern, um genauer zu sein.

Ein anderes logistisches Problem, mit dem Sie möglicherweise konfrontiert werden, ist die Notwendigkeit, herauszufinden, wie Sie vorgehen müssen, wenn die CSV-Datei über Header verfügt.

+0

Ich werde das versuchen und zurückkommen, danke – django

-2

Sie könnten Lazy Reading verwenden, um dies zu beschleunigen, die gesamte Datei wird nicht gelesen, nur vom Anfang der Datei bis zum verwendeten Chunk. Beispiele siehe http://engineering.continuity.net/csv-tricks/ und https://reinteractive.com/posts/154-improving-csv-processing-code-with-laziness.

Sie könnten auch SmarterCSV verwenden, um in solchen Chunks zu arbeiten.

SmarterCSV.process(file_path, {:chunk_size => 1000}) do |chunk| 
    chunk.each do |row| 
    # Do your processing 
    end 
    do_something_else 
end 
enter code here 

So wie ich dies tat, war durch das Ergebnis an den Benutzer Streaming, wenn Sie sehen, was es so viel nicht stört geschieht Sie warten müssen. Die von Ihnen angegebene Zeitüberschreitung wird hier nicht auftreten. Ich bin kein Rails-Benutzer, also gebe ich ein Beispiel aus Sinatra, das kann man auch mit Rails machen. Siehe zB http://api.rubyonrails.org/classes/ActionController/Streaming.html

require 'sinatra' 

get '/' do 
    line = 0 
    stream :keep_open do |out| 
    1.upto(100) do |line| # this would be your CSV file opened 
     out << "processing line #{line}<br>" 
     # process line 
     sleep 1 # for simulating the delay 
    end 
    end 
end 

Noch besser aber etwas komplizierte Lösung wäre WebSockets zu verwenden, würde der Browser die Ergebnisse vom Server erhalten, sobald die Verarbeitung abgeschlossen ist. Sie benötigen auch Javascript im Client, um dies zu handhaben. Siehe https://github.com/websocket-rails/websocket-rails