James Edward Gray II said: > On Nov 7, 2004, at 12:18 PM, Dennis Ranke wrote: > >> Here is a very simple solution. It doesn't try to understand much of >> the contents of the .ged file, it just builds a tree of nodes and >> dumps them to xml. > > I took a similar approach, parse and print. My code doesn't really > understand GEDCOM. Right. No real understanding, although I did implement a few enhancments discussed on the list (see comments for details). I used my XML Builder to generate the XML, so the approach is parse with interwoven printing. -- BEGIN CODE -------------------------------------------------------- #!/usr/bin/env ruby ###################################################################### # # RubyQuiz #6: Convert a GED Geneological file to XML # # This implements the quiz requirements with the following exceptions: # # 1. Nodes that have a @xxx@ reference style value put the data in a # 'ref' attribute rather than a straight data. # 2. Child nodes of type CONC or CONT are concatenated to the data # value of their parent node. # # Test data can be found at: # http://search.cpan.org/src/PJCJ/Gedcom-1.11/royal.ged begin require 'rubygems' rescue LoadError => ex end require 'builder' ###################################################################### # Read a GED file line by line. Each line is broken into three # tokens: # * level (integer) -- Nesting level of node. # * tag (string) -- Node tag. # * data -- Data on line after tag (can be empty or nil). # class GedReader attr_reader :level, :tag, :data, :line # Initialize the GED reader to read the given file by name. def initialize(file_name) @file = open(file_name) advance end # Advance the reader to the next non-blank line. def advance @level = -1 loop do read_line return if @line.nil? break if @line !~ /^\s*$/ end @level, @tag, @data = @line.chomp.split(/\s+/, 3) @level = @level.to_i end # Return the line broken into the three components. def info [@level, @tag, @data] end # Read the next line of the GED file. def read_line @line = @file.gets @file.close if @line.nil? end end ###################################################################### # GedParser ... Translate a GED data file in to a XML representation. # class GedParser attr_reader :xml # Initialize a GED parser. def initialize(filename) @reader = GedReader.new(filename) @builder = Builder::XmlMarkup.new(:indent=>2) @builder.gencom { parse } @xml = @builder.target! end # Are there children to this node? def children?(this_level) @reader.level > this_level end # Is the data a reference key (e.g. "@xx@")? def ref?(data) data =~ /^@.*@$/ end # Does the data exist? (i.e. not nil and not empty) def exist?(data) (! data.nil?) && (! data.empty?) end # Is the data continued in the next data item? def continued?(level) @reader.level == (level+1) && @reader.tag =~ /^(cont|conc)$/i end # Parse the next chunck of the GED file consisting of all GED # elements at the current level. Children of elements of the # current level are handled recursively. All elements parsed will # be added to the XML builder. def parse level = @reader.level while @reader.level && @reader.level == level lev, tag, data = @reader.info @reader.advance # The default arguments to the XML builder will use the tag # value as the XML tag. xml_tag = tag.downcase attrs = {} # Concatendate any lower level continued data. while data && @reader.level && continued?(level) data << (@reader.data || "") << "\n" @reader.advance end # If there are children, we will parse them in a block passed to # the builder. block = children?(level) ? lambda { parse } : nil # if the tag is a @xx@ reference, then the data becomes the tag # and the reference is passed as an 'id' attributes. if ref?(tag) xml_tag = data.downcase data = nil attrs['id'] = tag end # If the data is a @xx@ reference, then pass it as a 'ref' # attribute rather than a data value. if ref?(data) attrs['ref'] = data data = nil end # if there are children, then pass the data as a value attribute. if children?(level) attrs['value'] = data if exist?(data) data = nil end # Construct the arguments to the XML builder and call it. args = [xml_tag] args << data if exist?(data) args << attrs if exist?(attrs) @builder.tag!(*args, &block) end end end if __FILE__ == $0 then puts GedParser.new( ARGV[0] || 'royal.ged' ).xml end -- -- Jim Weirich jim / weirichhouse.org http://onestepback.org ----------------------------------------------------------------- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)