2010-12-10 27 views

Respuesta

92

La biblioteca Ruby CSV le permite especificar el delimitador de campo. Ruby 1.9 usa FasterCSV. Algo como esto funcionaría:

require "csv" 
parsed_file = CSV.read("path-to-file.csv", { :col_sep => "\t" }) 
+4

Tenga en cuenta que este enfoque fallará si alguno de los valores separados por tabuladores contiene una comilla doble. La sugerencia de StrictTsv en la otra respuesta es más sólida. –

23

Las reglas para TSV son en realidad un poco diferentes de CSV. La principal diferencia es que CSV tiene disposiciones para pegar una coma dentro de un campo y luego usar los caracteres de comillas y las comillas de escape dentro de un campo. Escribí un rápido ejemplo para mostrar cómo la respuesta sencilla falla:

require 'csv' 
line = 'boogie\ttime\tis "now"' 
begin 
    line = CSV.parse_line(line, col_sep: "\t") 
    puts "parsed correctly" 
rescue CSV::MalformedCSVError 
    puts "failed to parse line" 
end 

begin 
    line = CSV.parse_line(line, col_sep: "\t", quote_char: "Ƃ") 
    puts "parsed correctly with random quote char" 
rescue CSV::MalformedCSVError 
    puts "failed to parse line with random quote char" 
end 

#Output: 
# failed to parse line 
# parsed correctly with random quote char 

Si desea utilizar la biblioteca CSV se podía utilizado un carácter cita al azar que usted no espera para ver si el archivo (en el ejemplo esto), pero también podría usar una metodología más simple como la clase StrictTsv que se muestra a continuación para obtener el mismo efecto sin tener que preocuparse por las citas de campo.

# The main parse method is mostly borrowed from a tweet by @JEG2 
class StrictTsv 
    attr_reader :filepath 
    def initialize(filepath) 
    @filepath = filepath 
    end 

    def parse 
    open(filepath) do |f| 
     headers = f.gets.strip.split("\t") 
     f.each do |line| 
     fields = Hash[headers.zip(line.split("\t"))] 
     yield fields 
     end 
    end 
    end 
end 

# Example Usage 
tsv = Vendor::StrictTsv.new("your_file.tsv") 
tsv.parse do |row| 
    puts row['named field'] 
end 

La opción de usar la biblioteca CSV o algo más estricta sólo depende de que le está enviando el archivo y si están esperando a que se adhieran a la estricta norma TSV.

Los detalles sobre el estándar TSV se pueden encontrar en http://en.wikipedia.org/wiki/Tab-separated_values

+0

Incluya fragmentos de código con la respuesta, * no * en una esencia externa. Esa idea ahora parece estar abajo, lo cual es una verdadera lástima. –

+4

@JezenThomas gracias por el aviso. Recopilé todas las muestras de código en línea para solucionar el problema de tener que ir a la gran respuesta de git – mmmries

+0

. . Me sorprende lo horrible que '\ d' con el analizador CSV falla. – dps

0

me gusta mmmries respuesta. SIN EMBARGO, odio la forma en que el rubí elimina cualquier valor vacío del final de una división. Tampoco se está quitando la nueva línea al final de las líneas.

Además, tenía un archivo con nuevas líneas potenciales dentro de un campo. Por lo tanto, me volvió a escribir su 'análisis sintáctico' de la siguiente manera:

def parse 
    open(filepath) do |f| 
    headers = f.gets.strip.split("\t") 
    f.each do |line| 
     myline=line 
     while myline.scan(/\t/).count != headers.count-1 
     myline+=f.gets 
     end 
     fields = Hash[headers.zip(myline.chomp.split("\t",headers.count))] 
     yield fields 
    end 
    end 
end 

Este concatena todas las líneas como sea necesario para obtener una línea completa de datos, y siempre devuelve el conjunto completo de datos (sin entradas potenciales nil al final).

Cuestiones relacionadas