我需要能够在我的Ruby项目中找出csv文件(逗号,空格或分号)中使用的分隔符。我知道,csv模块中的Python中有一个Sniffer类可用于猜测给定文件的分隔符。 Ruby中有类似的东西吗?非常感谢任何形式的帮助或想法。
答案 0 :(得分:9)
看起来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
。
答案 1 :(得分:8)
我们在制作中使用它时,这是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
答案 2 :(得分:2)
我不知道Ruby 1.9中包含的CSV库中有任何嗅探器实现。它将尝试自动发现行分隔符,但默认情况下假定列分隔符为逗号。
一个想法是尝试使用每个可能的分隔符解析一个样本行数(总数的5%?)。无论哪个分隔符产生相同数量的列,最一致的可能是正确的分隔符。