2011-01-11 41 views
14

Tengo un formulario (Rails) que me permite cargar un archivo .csv usando el file_field. En la vista:¿Cuenta la longitud (número de líneas) de un archivo 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 %> 

clic en Enviar me redirige a otra página (create.html.erb). El archivo se cargó bien, y pude leer el contenido muy bien en esta segunda página. Estoy tratando de mostrar el número de líneas en el archivo .csv en esta segunda página.

Mi controlador (semi-pseudocódigo):

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 

Ambos file.length y file.size devuelve '91' cuando mi archivo sólo contiene 7 líneas. De la documentación de Rails que leí, una vez que se hace clic en el botón Enviar, Rails crea un archivo temporal del archivo cargado, y el params[:upcsv][:filename] contiene el contenido del archivo temp/upload y no el ruta al archivo. Y no sé cómo extraer el número de líneas en mi archivo original. ¿Cuál es la forma correcta de obtener el número de líneas en el archivo?

Mi create.html.erb:

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

estoy realmente nuevo en carriles (acaba de empezar la semana pasada), así que por favor tengan paciencia mis preguntas tontas.

¡Gracias!

Actualización: aparentemente ese número '91' es el número de caracteres individuales (incluido el retorno de carro) en mi archivo. Cada línea en mi archivo tiene 12 dígitos + 1 línea nueva = 13. 91/13 = 7.

+0

Sea muy cuidadoso que permite que un archivo sea subido sin algunas pruebas en tamaño del archivo. Imagine los problemas si el archivo usa todo el espacio de disco en su disco. O bien, si el archivo tiene muchos gigabytes de retornos de carro, y su código en Rails está girando tratando de leer y contar las líneas, DOSing su host. Si está en Linux, es posible que desee que el comando 'wc' del sistema operativo lo levante, ya que puede devolver el número de líneas y el número de caracteres en el archivo muy rápidamente, sin que Rails tenga que abrirlo y leerlo. –

Respuesta

12

otra manera de leer el número de líneas se

file.readlines.size 
+0

¡Oye, eso realmente funciona!Sin embargo, Rails eliminó el Tempfile después de ejecutar esa línea, por lo que no puedo procesar el contenido del archivo ... comportamiento extraño. ¡Gracias! – Mathias

+0

¡De nada! – gicappa

+1

@Mathias, ¿estás seguro de que se eliminó el archivo Tempfile? Sospecho que solo necesita rebobinar ('file.seek (0)') – cam

16

.length y .size son en realidad sinónimos. para obtener el recuento de filas del archivo csv, tienes que analizarlo realmente. simplemente contar las nuevas líneas en el archivo no funcionará, porque los campos de cadena en un csv en realidad pueden tener saltos de línea.una forma sencilla de obtener la lineatura sería:

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

¡Gracias, muchachos! Por desgracia, ahora me aparece "no puedo convertir el archivo temporal en cadena". Este es el parámetro de solicitud: {"commit" => "Submit", "authenticity_token" => "<-removed->", "upcsv" => {"filename" => # }} ¿Hay alguna forma de que pueda evaluar el archivo .csv real en lugar de este archivo temporal? – Mathias

0

Sólo para demostrar lo que IO # readlines hace:

si has tenido una presentar la siguiente manera: "asdflkjasdlkfjsdakf \ n asdfjljdaslkdfjlsadjfasdflkj \ n asldfjksdjfa \ n"

en los carriles que le hacemos, decimos:

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

IO # readlines convierte un archivo en una matriz de cadenas utilizando el \ n (saltos de línea) como separadores, al igual que comas tan a menudo lo hacen, por lo que es, básicamente, como

str.split(/\n/) 

De hecho, si se hizo

x = file.read 

este

x.split(/\n/) 

haría lo mismo que file.readlines

** IO # readlines pueden ser realmente útil cuando se trata de archivos que tengan una estructura de la línea de repetición ("child_id", "parent_ary", "child_id", "parent_ary ", ...) etc.

+0

** para hacer lo anterior en rieles, algo como esto ("config.autoload_paths + = Dir [" # {config.root}/lib/**/"]") se debe agregar a config/application.rb –

16

Todas las soluciones enumeradas aquí en realidad cargan todo el archivo en la memoria para obtener el número de líneas. Si estás en un sistema basado en Unix, un mucho más rápido, más fácil y solución eficaz memoria es:

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

A fila en un archivo CSV puede contener líneas nuevas, necesita analizarlo realmente. –

0

Si el archivo CSV no se ajusta a la memoria (no se puede utilizar readlines), se puede hacer :

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

a diferencia de esto cuenta wc -l recuento de registros real, no el número de líneas. Estos pueden ser diferentes si hay nuevas líneas en los valores de campo.

+0

Es una buena idea, pero readlines devuelve un enumerador, por lo que no debe leer todo en la memoria, de todos modos. –

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

Por encima se exclue cabecera contando filas

CSV.read(file_path).count 
+0

'CSV.read (file_path, headers: true) .count' también debe devolver el conteo excluyendo el encabezado – chetang

Cuestiones relacionadas