2012-04-12 3 views
0

Ich habe eine CSV, die ich gerne alle meine Hash-Werte darauf speichern. Ich benutze Nokogiri Sax, um ein XML-Dokument zu analysieren und dann in einer CSV-Datei zu speichern.Konvertieren von Hashes in CSV

Der SAX-Parser:

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 

neu ein Objekt für jede Datei erstellen, speichern Sie in einem Ordner (47.000xml Dateien):

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 

Das Problem: I dont get eine neue Zeile für jede neue Menge von Werten. Irgendwelche Ideen?

3 XML-Dateien für den Code versuchen: https://gist.github.com/2378898 https://gist.github.com/2378901 https://gist.github.com/2378904

+2

I‘ Ich sage es noch einmal, 'b' Modus macht keinen Sinn mit CSV-Daten. – pguardiario

Antwort

3

Sie benötigen zum Öffnen der Datei mit „a“ Modus (eine Datei mit „w“ Öffnen löscht alle vorherigen Inhalte).

Wenn Sie ein Array an das CSV-Objekt anfügen, werden automatisch Zeilenumbrüche eingefügt. Hash # -Werte gibt ein Array der Werte zurück, aber es wäre sicherer, die Reihenfolge zu erzwingen. Das Abflachen des Arrays führt möglicherweise zu falsch ausgerichteten Spalten (z. B. [[: title1,: title2], 'anderer Wert'] führt zu [: title1,: title2, 'anderer Wert']). Probieren Sie etwas wie folgt aus:

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 

Nachdem die oberhalb der infodata.csv Datei ausgeführt wird enthalten:

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 

Die obige Änderung durch Ausführen der folgenden überprüft werden kann

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

Hallo Kumpel, dein Code macht dasselbe wie mein Code. Und erstellt keine neue Zeile für jede neue Menge von Werten – SHUMAcupcake

+0

Welche Version von Ruby verwenden Sie? Das Ändern des Dateimodus auf "ab" funktioniert bei mir sowohl mit 1.9.2p290 als auch 1.9.3-p0. Öffnet Ihr Code infodata.csv im Schreibmodus an einem anderen Ort? Ich werde die Antwort aktualisieren, um den Code zu enthalten, der zum Überprüfen des Fixes verwendet wurde. – cydparser

+0

Ich benutze ruby ​​v 1.8.7, ich bekomme einen ArgumentError: 'mode' muss 'r', 'rb', 'w' oder 'wb' sein – SHUMAcupcake

1

Ich denke, man eine zusätzliche Schleife benötigen. Etwas ähnliches wie

CSV.open("infodata.csv", "wb") do |csv|  
    csv << @infodata.keys 
    @infodata.each do |key, value| 
    csv << value 
    end 
end