Ruby:如何检测/智能猜测CSV文件中使用的分隔符?

我需要能够在我的Ruby项目中找出csv文件(逗号,空格或分号)中使用的分隔符。 我知道,csv模块中的Python中有一个Sniffer类可用于猜测给定文件的分隔符。 Ruby中有类似的东西吗? 非常感谢任何forms的帮助或想法。

看起来py实现只检查几个方言:excel或excel_tab。 因此,只需检查",""\t"的简单实现是:

 COMMON_DELIMITERS = ['","',"\"\t\""] def sniff(path) first_line = File.open(path).first return nil unless first_line snif = {} COMMON_DELIMITERS.each {|delim|snif[delim]=first_line.count(delim)} snif = snif.sort {|a,b| b[1]<=>a[1]} snif.size > 0 ? snif[0][0] : nil end 

注意:这将返回它找到的完整分隔符,例如"," ,因此要获得,您可以将snif[0][0]更改为snif[0][0][1]

另外,我使用count(delim)因为它有点快,但是如果你添加了一个由两个(或更多)相同类型的字符组成的分隔符,例如-- ,那么它可能每次出现两次(或者更多)在称重类型时,所以在这种情况下,最好使用scan(delim).length

这是Gary S. Weaver的答案,因为我们在生产中使用它。 好的解决方案,运作良好。

 class ColSepSniffer NoColumnSeparatorFound = Class.new(StandardError) EmptyFile = Class.new(StandardError) COMMON_DELIMITERS = [ '","', '"|"', '";"' ].freeze def initialize(path) @path = path end def self.find(path) new(path: path).find end def find fail EmptyFile unless first if valid? delimiters[0][0][1] else fail NoColumnSeparatorFound end end private def valid? !delimiters.collect(&:last).reduce(:+).zero? end # delimiters #=> [["\"|\"", 54], ["\",\"", 0], ["\";\"", 0]] # delimiters[0] #=> ["\";\"", 54] # delimiters[0][0] #=> "\",\"" # delimiters[0][0][1] #=> ";" def delimiters @delimiters ||= COMMON_DELIMITERS.inject({}, &count).sort(&most_found) end def most_found ->(a, b) { b[1] <=> a[1] } end def count ->(hash, delimiter) { hash[delimiter] = first.count(delimiter); hash } end def first @first ||= file.first end def file @file ||= File.open(@path) end end 

规格

 require "spec_helper" describe ColSepSniffer do describe ".find" do subject(:find) { described_class.find(path) } let(:path) { "./spec/fixtures/google/products.csv" } context "when , delimiter" do it "returns separator" do expect(find).to eq(',') end end context "when ; delimiter" do let(:path) { "./spec/fixtures/google/products_with_semi_colon_seperator.csv" } it "returns separator" do expect(find).to eq(';') end end context "when | delimiter" do let(:path) { "./spec/fixtures/google/products_with_bar_seperator.csv" } it "returns separator" do expect(find).to eq('|') end end context "when empty file" do it "raises error" do expect(File).to receive(:open) { [] } expect { find }.to raise_error(described_class::EmptyFile) end end context "when no column separator is found" do it "raises error" do expect(File).to receive(:open) { [''] } expect { find }.to raise_error(described_class::NoColumnSeparatorFound) end end end end 

我不知道Ruby 1.9中包含的CSV库中有任何嗅探器实现。 它将尝试自动发现行分隔符,但默认情况下假定列分隔符为逗号。

一种想法是尝试使用每个可能的分隔符解析样本数量的行(总数的5%?)。 无论哪个分隔符导致相同数量的列最一致,可能是正确的分隔符。