sub unescape { my $x = $_[0]; my ($base, $end) = $x =~ /^(.*)(!+)\z/s; return $base . ('!' x (length($end)/2)); } my $last_field = qr/ [^;]* /x; my $other_field = qr/ (?: [^!]+ | (?: ![^!] )+ )* (?:!!)* /x; # Validation my $record = qr/^ (?: $other_field ; )* $last_field \z/x; # Extraction my @fields = map unescape($_), / \G ( $other_field (?= ; ) | $last_field (?= \z ) ) (?:;|\z) /xg;