Fairly general solution:
my $input = 'XXI3|XI39|MP0@1 XXI3|XI39|MP0@2 XXI3|XI39|MP0@3 XXI3|XI39|MP0@4 XXI3|XI39|MP0@5';
my %hash; # output
for my $word (split " ", $input) {
my ($path, $value) = split /\@/, $word, 2;
my @path = split /\|/, $path;
my $last = pop @path;
my $target = \%hash;
for my $path_elem (@path) {
$target = ($target->{$path_elem} ||= {});
}
push @{ $target->{$last} }, $value;
}
First, break up the input into words, and process one word at a time.
Then separate the "path" and "value" portions of the word by splitting on @. Split the path into its components on |. Remove the last component of the path and save it for later.
Take a reference to the top of the data structure, and for each component in the path (except for the last), go down one level, creating that level as an empty hash if it doesn't already exist.
For the last path component, push onto an array reference (which will be created by autovivification if necessary).
Result:
{
'XXI3' => {
'XI39' => {
'MP0' => [
'1',
'2',
'3',
'4',
'5'
]
}
}
};