2011-01-11 3 views
14

У меня есть форма (Rails), которая позволяет мне загрузить CSV-файл, используя file_field. По мнению:Подсчитайте длину (количество строк) файла CSV?

<% form_for(:upcsv, :html => {:multipart => true}) do |f| %> 
    <table> 
     <tr> 
      <td><%= f.label("File:") %></td> 
      <td><%= f.file_field(:filename) %></td> 
     </tr> 
    </table> 
     <%= f.submit("Submit") %> 
    <% end %> 

Щелчок Submit перенаправляет меня на другую страницу (create.html.erb). Файл был загружен отлично, и я смог прочитать содержимое на этой второй странице. Я пытаюсь показать количество строк в CSV-файле на этой второй странице.

Мой контроллер (полу-псевдокод):

class UpcsvController < ApplicationController 
    def index 
    end 

    def create 
     file = params[:upcsv][:filename] 
     ... 
     #params[:upcsv][:file_length] = file.length # Show number of lines in the file 
     #params[:upcsv][:file_length] = file.size 
     ... 
    end 
end 

Обе file.length и file.size возвращает '91', когда мой файл содержит только 7 строк. Из документации Rails, которую я прочитал, после нажатия кнопки «Отправить» Rails создает временный файл загруженного файла, а params[:upcsv][:filename] содержит содержимое файла temp/uploaded, а не путь к файлу. И я не знаю, как извлечь количество строк в моем исходном файле. Каков правильный способ получить количество строк в файле?

Мой create.html.erb:

<table> 
    <tr> 
     <td>File length:</td> 
     <td><%= params[:upcsv][:file_length] %></td> 
    </tr> 
</table> 

Я действительно новичок в Rails (только начал на прошлой неделе), поэтому, пожалуйста, медведь со своими глупыми вопросами.

Спасибо!

Обновление: Очевидно, что число '91' - это количество отдельных символов (включая возврат каретки) в моем файле. Каждая строка в моем файле имеет 12 цифр + 1 новая строка = 13. 91/13 = 7.

+0

Будьте осторожны реальным позволяя файл будет загружен без каких-либо тестов на размер_файле. Представьте себе проблемы, если файл использует все дисковое пространство на вашем диске. Или, если в файле много гигабайт возврата каретки, и ваш код в Rails вращается, пытаясь прочитать и подсчитать строки, DOSing вашего хоста. Если вы находитесь в Linux, вы можете захотеть, чтобы команда 'wc' OS выполняла для вас подъем, так как она могла быстро возвращать количество строк и количество символов в файле, без необходимости открывать и читать Rails. –

ответ

12

другой способ, чтобы прочитать число строк

file.readlines.size 
+0

Эй, это действительно работает!Однако Rails удалил Tempfile после запуска этой строки, поэтому я не могу обработать содержимое файла ... странное поведение. Спасибо! – Mathias

+0

Добро пожаловать! – gicappa

+1

@Mathias, вы уверены, что Tempfile удален? Я подозреваю, что вам просто нужно перемотать ('file.seek (0)') – cam

16

. Длина и .size на самом деле являются синонимами. чтобы получить номер строки csv-файла, который вы должны проанализировать. просто подсчет строк в файле не будет работать, потому что в строковых полях в csv могут быть разрывы строк.простой способ получить LineCount бы:

CSV.read(params[:upcsv][:filename]).length 
+0

Спасибо, ребята! Увы, теперь я получаю «невозможно преобразовать Tempfile в String». Это параметр запроса: {"commit" => "Отправить", "authenticity_token" => "<-removed->", "upcsv" => {"filename" => # <Файл:/tmp/RackMultipart20110111-14030-142mv1a-0 >}} Есть ли способ, которым я могу оценить фактический .csv-файл, а не этот Tempfile? – Mathias

0

Просто, чтобы продемонстрировать, что IO # readlines делает:

если вы имели файл, как это: "asdflkjasdlkfjsdakf \ п asdfjljdaslkdfjlsadjfasdflkj \ п asldfjksdjfa \ п"

в рельсах вы могли бы сделать, сказать:

file = File.open(File.join(Rails.root, 'lib', 'file.json')) 
lines_ary = IO.readlines(file) 
lines_ary.count #=> 3 

IO # readlines преобразует файл в массив строк с помощью \ п (строки) в качестве разделителей, так же, как запятые так часто, так что это в основном, как

str.split(/\n/) 

В самом деле, если вы сделали

x = file.read 

это

x.split(/\n/) 

будет делать то же самое, как file.readlines

** IO # readlines может быть очень удобно при работе с файлами, которые имеют повторяющуюся структуру линии («child_id», «parent_ary», «child_id», «parent_ary ", ...) и т. д.

+0

**, чтобы сделать выше в рельсах, что-то вроде этого («config.autoload_paths + = Dir [" # {config.root}/lib/**/"]") необходимо добавить в config/application.rb –

16

Все перечисленные здесь решения фактически загружают весь файл в память, чтобы получить количество строк. Если вы на системе Unix на основе гораздо быстрее, проще и памяти эффективным решением является:

`wc -l #{your_file_path}`.to_i 
+0

A строка в CSV может содержать символы новой строки, вам необходимо проанализировать ее. –

0

Если файл CSV не помещается в памяти (не может использовать readlines), вы можете сделать :

def self.line_count(f) 
    i = 0 
    CSV.foreach(f) {|_| i += 1} 
    i 
end 

в отличие от wc -l это считается фактическое количество записей, а не количество строк. Они могут быть разными, если в значениях полей есть новые строки.

+0

Это хорошая идея, но readlines возвращает перечислитель, поэтому в любом случае он не должен читать все это в памяти. –

3
CSV.foreach(file_path, headers: true).count 

Выше будет exclue заголовка при подсчете строк

CSV.read(file_path).count 
+0

'CSV.read (file_path, headers: true) .count' также должен возвращать счетчик, исключающий заголовок – chetang

Смежные вопросы