2010-12-10 7 views

Répondre

92

La bibliothèque Ruby CSV vous permet de spécifier le délimiteur de champ. Ruby 1.9 utilise FasterCSV. Quelque chose comme ceci fonctionnerait:

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

Attention, cette approche échouera si l'une des valeurs séparées par des tabulations contient un guillemet. La suggestion StrictTsv dans l'autre réponse est plus robuste. –

23

Les règles pour TSV sont en fait un peu différentes de CSV. La principale différence est que CSV a des dispositions pour coller une virgule à l'intérieur d'un champ, puis en utilisant des guillemets et des guillemets d'échappement à l'intérieur d'un champ. J'ai écrit un exemple rapide pour montrer comment la simple réponse échoue:

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 vous souhaitez utiliser la bibliothèque CSV, vous pouvez utiliser un caractère de citation aléatoire que vous ne vous attendez pas à voir si votre fichier (l'exemple montre ceci), mais vous pouvez également utiliser une méthode plus simple comme la classe StrictTsv montrée ci-dessous pour obtenir le même effet sans avoir à vous soucier des citations de terrain.

# 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 

Le choix d'utiliser la bibliothèque CSV ou quelque chose de plus stricte dépend juste de qui vous envoie le fichier et s'ils attendent d'adhérer à la norme stricte de TSV.

Détails sur la norme TSV se trouvent à http://en.wikipedia.org/wiki/Tab-separated_values

+0

Veuillez inclure des extraits de code avec la réponse, * not * dans un aperçu externe. Cet aspect semble maintenant être en baisse, ce qui est une véritable honte. –

+4

@JezenThomas merci pour les heads-up. J'ai tiré tous les échantillons de code en ligne pour résoudre le problème d'avoir à aller voir l'essentiel – mmmries

+0

bonne réponse. . Je suis surpris de voir comment horriblement '\ d' échoue avec l'analyseur CSV. – dps

0

J'aime mmmries réponse. Cependant, je déteste la façon dont ruby ​​enlève toutes les valeurs vides de la fin d'une scission. Il ne s'agit pas non plus de supprimer le retour à la ligne à la fin des lignes.

En outre, j'avais un fichier avec des nouvelles lignes potentielles dans un champ. Alors, je réécris son « parse » comme suit:

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 

Cette concaténer toutes les lignes que nécessaire pour obtenir une gamme complète de données, et renvoie toujours l'ensemble des données (sans entrées néant potentiel à la fin).

Questions connexes