2012-04-12 4 views
0

У меня есть CSV, который мне нравится сохранять на нем все мои хэш-значения. Я использую nokogiri sax для анализа XML-документа и сохранения его в CSV.Преобразование хэшей в CSV

саксофоне анализатор:

require 'rubygems' 
require 'nokogiri' 
require 'csv' 

class MyDocument < Nokogiri::XML::SAX::Document 

    HEADERS = [ :titles, :identifier, :typeOfLevel, :typeOfResponsibleBody, 
       :type, :exact, :degree, :academic, :code, :text ] 

    def initialize 
    @infodata = {} 
    @infodata[:titles] = Array.new([]) 
    end 

    def start_element(name, attrs) 
    @attrs = attrs 
    @content = '' 
    end 
    def end_element(name) 
    if name == 'title' 
     Hash[@attrs]["xml:lang"] 
     @infodata[:titles] << @content 
     @content = nil 
    end 
    if name == 'identifier' 
     @infodata[:identifier] = @content 
     @content = nil 
    end 
    if name == 'typeOfLevel' 
     @infodata[:typeOfLevel] = @content 
     @content = nil 
    end 
    if name == 'typeOfResponsibleBody' 
     @infodata[:typeOfResponsibleBody] = @content 
     @content = nil 
    end 
    if name == 'type' 
     @infodata[:type] = @content 
     @content = nil 
    end 
    if name == 'exact'  
     @infodata[:exact] = @content 
     @content = nil 
    end 
    if name == 'degree' 
     @infodata[:degree] = @content 
     @content = nil 
    end 
    if name == 'academic' 
     @infodata[:academic] = @content 
     @content = nil 
    end 
    if name == 'code' 
     Hash[@attrs]['source="vhs"'] 
     @infodata[:code] = @content 
     @content = nil 
    end 
    if name == 'ct:text' 
     @infodata[:beskrivning] = @content 
     @content = nil 
    end 
    end 
    def characters(string) 
    @content << string if @content 
    end 
    def cdata_block(string) 
    characters(string) 
    end 
    def end_document 
    File.open("infodata.csv", "ab") do |f| 
     csv = CSV.generate_line(HEADERS.map {|h| @infodata[h] }) 
     csv << "\n" 
     f.write(csv) 
    end 
    end 
end 

создает новый объект для каждого файла, который следует хранить в папке (файлы 47.000xml):

parser = Nokogiri::XML::SAX::Parser.new(MyDocument.new) 
counter = 0 

Dir.glob('/Users/macbookpro/Desktop/sax/info_xml/*.xml') do |item| 
    parser.parse(File.open(item, 'rb')) 
    counter += 1 
    puts "Writing file nr: #{counter}" 
end 

выпуска: я не получаю новая строка для каждого нового набора значений. Есть идеи?

3 файлов XML для пробуя код: https://gist.github.com/2378898 https://gist.github.com/2378901 https://gist.github.com/2378904

+2

Я Повторю, режим «b» не имеет смысла с данными csv. – pguardiario

ответ

3

Вам нужно открыть файл с помощью «а» режим (открытие файла с «ш» очищает любое предыдущее содержимое).

Добавление массива к объекту csv будет автоматически вставлять новые строки. Значения Hash # возвращают массив значений, но было бы безопаснее форсировать порядок. Сглаживание массива потенциально приведет к несогласованным столбцам (например, [[: title1,: title2], «other-value»] приведет к [: title1,: title2, 'other-value']). Попробуйте что-то вроде этого:

HEADERS = [:titles, :identifier, ...] 

def end_document 
    # with ruby 1.8.7 
    File.open("infodata.csv", "ab") do |f| 
    csv = CSV.generate_line(HEADERS.map { |h| @infodata[h] }) 
    csv << "\n" 
    f.write(csv) 
    end 
    # with ruby 1.9.x 
    CSV.open("infodata.csv", "ab") do |csv| 
    csv << HEADERS.map { |h| @infodata[h] } 
    end 
end 

выше изменения могут быть проверены путем выполнения следующего:

require "csv" 

class CsvAppender 

    HEADERS = [ :titles, :identifier, :typeOfLevel, :typeOfResponsibleBody, :type, 
       :exact, :degree, :academic, :code, :text ] 

    def initialize 
    @infodata = { :titles => ["t1", "t2"], :identifier => 0 } 
    end 

    def end_document 
    @infodata[:identifier] += 1 

    # with ruby 1.8.7 
    File.open("infodata.csv", "ab") do |f| 
     csv = CSV.generate_line(HEADERS.map { |h| @infodata[h] }) 
     csv << "\n" 
     f.write(csv) 
    end 
    # with ruby 1.9.x 
    #CSV.open("infodata.csv", "ab") do |csv| 
    # csv << HEADERS.map { |h| @infodata[h] } 
    #end 
    end 

end 

appender = CsvAppender.new 

3.times do 
    appender.end_document 
end 

File.read("infodata.csv").split("\n").each do |line| 
    puts line 
end 

После запуска выше файла infodata.csv будет содержать:

"[""t1"", ""t2""]",1,,,,,,,, 
"[""t1"", ""t2""]",2,,,,,,,, 
"[""t1"", ""t2""]",3,,,,,,,, 
+0

Привет, ваш код делает то же самое, что и мой код. И не создает новую строку для каждого нового набора значений. – SHUMAcupcake

+0

Какую версию рубина вы используете? Изменение режима файла на «ab» работает для меня с 1.9.2p290 и 1.9.3-p0. Ваш код открывает infodata.csv в режиме записи в любом другом месте? Я обновлю ответ, чтобы включить код, используемый для проверки исправления. – cydparser

+0

Я использую ruby ​​v 1.8.7, я получаю ArgumentError: 'mode' должен быть 'r', 'rb', 'w' или 'wb' – SHUMAcupcake

1

Я думаю, вам нужен дополнительный цикл. Нечто похожее на

CSV.open("infodata.csv", "wb") do |csv|  
    csv << @infodata.keys 
    @infodata.each do |key, value| 
    csv << value 
    end 
end 
Смежные вопросы