मुझे यह पता लगाने में सक्षम होना चाहिए कि मेरे रूबी प्रोजेक्ट में सीएसवी फ़ाइल (कॉमा, स्पेस या अर्धविराम) में कौन सी डिलीमीटर का उपयोग किया जा रहा है। मुझे पता है, सीएसवी मॉड्यूल में पायथन में एक स्निफर वर्ग है जिसे किसी दिए गए फ़ाइल के डिलीमीटर का अनुमान लगाने के लिए उपयोग किया जा सकता है। क्या रूबी में ऐसा कुछ भी है? किसी भी तरह की मदद या विचार की सराहना की जाती है।रुबी: मैं CSV फ़ाइल में उपयोग किए गए डेलीमीटर का आकलन/बुद्धिमानी से कैसे अनुमान लगा सकता हूं?
उत्तर
ऐसा लगता है कि पाई कार्यान्वयन सिर्फ कुछ बोलीभाषाओं की जांच करता है: एक्सेल या एक्सेल_टैब। तो, कुछ ऐसा है जो सिर्फ ","
या "\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.9 में शामिल सीएसवी लाइब्रेरी में किसी भी स्निफ़र कार्यान्वयन से अवगत नहीं है। यह पंक्ति विभाजक को स्वत: खोजने का प्रयास करेगा, लेकिन स्तंभ विभाजक को डिफ़ॉल्ट रूप से अल्पविराम माना जाता है।
एक विचार संभावित संभावित विभाजकों का उपयोग करके पंक्तियों की एक नमूना संख्या (कुल का 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
तकनीकी तौर पर, उन में से केवल एक एक CSV फ़ाइल है ... –