fallenmonkey has asked for the wisdom of the Perl Monks concerning the following question:

Greetings, wise monks! I've got a text file in the following format:
<key 1> : <value 1> <key 2> : <value 2> .. <key n> : <value n>
Between each 1-to-n set of key/value pairs for a record, there is a single blank line. I'd like to turn this into a CSV file in the following format:
"key 1","key 2", .. ,"key n" "value 1","value 2", .. ,"value n" <-- record 1 "value 1","value 2", .. ,"value n" <-- record 2 .. "value 1","value 2", .. ,"value n" <-- record n
What do you all think is the most logical/efficient way to do this? For now, the file is very predictable. The keys for every record are the same. However, this will likely be a long-term solution and I'd like it to be able to handle changes in the number or names of the keys without breaking. I'm considering reading the original file into hashes and arrays, and using the Text::CSV module to generate the output. Just figured I'd call upon the monastery to point out flaws in that plan before implementing it. Thank you all!

The Fallen Monkey

Replies are listed 'Best First'.
Re: Record dump-style text --> CSV?
by Tanktalus (Canon) on Mar 09, 2005 at 22:36 UTC

    Many ways to do this. I'm going to read the text file into a data structure that mirrors the output, rather than mirroring what the input looks like. This way, I'm going to parse directly into the records. What I could have done was to read into a series of hashes instead. This just proves it's doable:

    use strict; use Text::CSV; my @data; my %header; { my $idx = 0; my @single_row; sub eos { if (@single_row) { push @data, [ @single_row ]; @single_row = (); } } sub index_for { my $key = shift; unless (exists $header{$key}) { $header{$key} = $idx++; } $header{$key}; } while(<DATA>) { chomp; if (/^$/) { # end of section eos(); } else { my ($key, $value) = split /\s*:\s*/, $_, 2; $single_row[index_for($key)] = $value; } } eos(); } my $csv = Text::CSV->new(); my @h = sort { $header{$a} <=> $header{$b} } keys %header; $csv->combine(@h); print $csv->string(), $/; $csv->combine(@$_), print $csv->string(), $/ foreach @data; __END__ a: a1 b: b1 c: c1 a: a2 b: b2 c: c2 a: a3 b: b3 c: c3 a: a4 b: b4 c: c4 d: d1
      Excellent, that works out very well! Just needed a couple modifications to handle input/output files and dos<-->unix filetype conversion, and now it's rocking. Thanks a lot!

      The Fallen Monkey