2013-02-04 45 views
5

मुझे यह पता लगाने में सक्षम होना चाहिए कि मेरे रूबी प्रोजेक्ट में सीएसवी फ़ाइल (कॉमा, स्पेस या अर्धविराम) में कौन सी डिलीमीटर का उपयोग किया जा रहा है। मुझे पता है, सीएसवी मॉड्यूल में पायथन में एक स्निफर वर्ग है जिसे किसी दिए गए फ़ाइल के डिलीमीटर का अनुमान लगाने के लिए उपयोग किया जा सकता है। क्या रूबी में ऐसा कुछ भी है? किसी भी तरह की मदद या विचार की सराहना की जाती है।रुबी: मैं CSV फ़ाइल में उपयोग किए गए डेलीमीटर का आकलन/बुद्धिमानी से कैसे अनुमान लगा सकता हूं?

+2

तकनीकी तौर पर, उन में से केवल एक एक CSV फ़ाइल है ... –

उत्तर

9

ऐसा लगता है कि पाई कार्यान्वयन सिर्फ कुछ बोलीभाषाओं की जांच करता है: एक्सेल या एक्सेल_टैब। तो, कुछ ऐसा है जो सिर्फ "," या "\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 का उपयोग करना बेहतर हो सकता है।

2

मुझे रूबी 1.9 में शामिल सीएसवी लाइब्रेरी में किसी भी स्निफ़र कार्यान्वयन से अवगत नहीं है। यह पंक्ति विभाजक को स्वत: खोजने का प्रयास करेगा, लेकिन स्तंभ विभाजक को डिफ़ॉल्ट रूप से अल्पविराम माना जाता है।

एक विचार संभावित संभावित विभाजकों का उपयोग करके पंक्तियों की एक नमूना संख्या (कुल का 5%?) को पार्स करने का प्रयास करना होगा। जो भी विभाजक परिणाम कॉलम की एक ही संख्या में सबसे अधिक लगातार परिणामस्वरूप सही विभाजक है।

5

यहां 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