0

data.txt

 Name:xyz
 ID:1
 Value: 1 2 3 4 5 6 7 8 9 ...
 ID:2 
 Value: 9 8 7 6 5 4 3 2 1..
 ID:3
 Value: 90 89 88....
 Name:abc
 ID:11
 value:...

Intial file.txt

## Header 
..
data
data
data
..

Final expected file.txt

## Header xyz_1,xyz_2,xyz_3,abc_11,...
..
data 1 9 90
data 2 8 89
data 3 7 88
data 4 6 
..

Current output file.txt

## Header xyz_1,xyz_2,xyz_3,abc_11,...
...
data, 1 2 3 4 5 6 7 8 9 ..,9 8 7 6 5 4 3 2 1 ..,90 89 88
data
data
...

Code

#!/usr/local/bin/perl
use diagnostics;
use strict;
use warnings;

use Tie::File;

my @name_id;
my %test;

#local $/ = '';

open my $fh, '<', 'data.txt' or die "failed: $!";
my %var;
while (<$fh>) {
  chomp;
  if (m/^([A-Z:]+):\s*(.*)/) {
    $var{$1} = $2;
    if (exists($var{Name}) && exists($var{ID}) && exists($var{value}) && $1 eq 'value') {
      my $var_name = "$var{Name}_$var{ID}";
      push @name_id, $var_name;
      $test{$var_name} = $var{value};
    }
  }
}

#   print join "\n\t", @test{@name_id};
my $match = "## Header";
tie my @lines, 'Tie::File', 'file.txt' or die "failed : $!";
for my $line (@lines) {
  if ($line =~ /^($match.*)/) {
    $line = $1 . "," . join ',', @name_id;
  }
}
untie @lines;

my $match = "data";
tie my @lines, 'Tie::File', 'file.txt' or die "failed : $!";
my $i = 0;
for my $line (@lines) {
  if ($line =~ /^($match.*)/) {
    $line = $1 . "," . join(',', map { $test{$_}->[$i] } @name_id);
    $i++;
  }
}
untie @lines;

Have a problem with this line $line = $1 . "," . join (',', map { $test{$_}->[$i]} @name_id); it throws the error

Can't use string ("1 2 3 4 5 6 7 8 9 .."...) as an ARRAY ref while "strict refs" in use at test.pl line 46, line 80. at test.pl line 46

I think the hash(%test) value I had is a string and I can't split it as an array. Please let me know how to convert it to an array. I tried doing $test{$var_name} = [qw($var{value})]; it didnt work.

1
  • Why would you mix $i and $1 in the same code together? Why :(. Please rename. Commented Jun 4, 2013 at 22:52

3 Answers 3

3

You may be interested in this refactoring of your code that seems to do what you want.

#!/usr/local/bin/perl
use strict;
use warnings;

use Tie::File;

open my $fh, '<', 'data.txt' or die "failed: $!";

my @name_id;
my %test;
my %var;

while (<$fh>) {
  chomp;
  if (my ($key, $val) = /^(\w+):\s*(.*)/) {
    $var{$key} = $val;
    if ($key eq 'value') {
      my $var_name = "$var{Name}_$var{ID}";
      push @name_id, $var_name;
      $test{$var_name} = [ split ' ', $var{value} ];
    }
  }
}

tie my @lines, 'Tie::File', 'file.txt' or die "failed : $!";
my $count = 0;
for my $line (@lines) {
  if ($line =~ /^## Header/) {
    $line .= ' ' . join ',', @name_id;
  }
  elsif ($line =~ /^data/) {
    $line .= ' ' . join ' ', map { $test{$_}[$count] // '' } @name_id;
    $count++;
  }
}
untie @lines;

output (file.txt)

## Header xyz_1,xyz_2 ,xyz_3
data 1 9 90
data 2 8 89
data 3 7 88
data 4 6 
Sign up to request clarification or add additional context in comments.

Comments

1

This is surely not right:

$test{$_}->[$i]

Because $test{$_} can only contain a string of some sort.

If you have a string and want to split into an arrayref so the above works, do this:

$test{$var_name} = [split /\s+/, $var{value}];

I have no idea what the code is supposed to accomplish which means that it may run, but I can't tell if it does what it is meant to. The odd variable names (like $test and $var_name didn't help me to understand the purpose).

2 Comments

it worked. Thats what I need although it complains for ` $line = $1 . "," . join (',', map { $test{$_}->[$i]} @name_id);` Use of uninitialized value in join or string at test4.pl line 47, <$fh> line 141 (#1)
Maybe you can reopen a new question with the new code. I still think you should clean up the variable names, by the way.
0

I'm not too sure I followed your code, but I thought I'd post how to transpose the numbers (unless your code already does that :-) ).

#!/usr/bin/perl
use strict;
use warnings;

my (%data, $name);
while (<DATA>) {
    if (/^Name:(.+)/) {
        $name = $1  
    }
    elsif (/^Value/) {
        # transpose
        my $r = 0;
        push @{ $data{$name}[$r++] }, $_ for /\d+/g;
    }   
}

use Data::Dumper; print Dumper \%data;

__DATA__
Name:xyz
ID:1
Value: 1 2 3 4 5 6 7 8 9
ID:2 
Value: 9 8 7 6 5 4 3 2 1
ID:3
Value: 90 89 88 87 86 85 84 83 82
Name:abc
ID:11

The dumped results are:

$VAR1 = {
          'xyz' => [
                     [
                       '1',
                       '9',
                       '90'
                     ],
                     [
                       '2',
                       '8',
                       '89'
                     ],
                     [
                       '3',
                       '7',
                       '88'
                     ],
                     [
                       '4',
                       '6',
                       '87'
                     ],
                     [
                       '5',
                       '5',
                       '86'
                     ],
                     [
                       '6',
                       '4',
                       '85'
                     ],
                     [
                       '7',
                       '3',
                       '84'
                     ],
                     [
                       '8',
                       '2',
                       '83'
                     ],
                     [
                       '9',
                       '1',
                       '82'
                     ]
                   ]
        };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.