追記 完全版

↑のコードだと不完全ですよう、とトラックバックを頂いたので、実際業務で使ったコード(の該当ロジック部分を抜粋)を晒しておきます。入力文字コードのチェックが厳密になっています。あと、splitを12でlimitしたことで更に40%位(うろ覚え)早いです。

リファラのデコードに対応していないのも惜しい。

は全てのリファラの元の文字コードを正確に判別るするのは非常にコストが高く、精度も出ないので止めた覚えが。膨大なログを処理する必要があったので、端折ったような気がします。

リファラがある場合とない場合とで検索語のフィールドが変わるから

の部分はよく分からなかった(リファラがあるから検索語があるような。。)のですが、まー大体これでいけますよ。

#!/usr/bin/perl
use strict;
use warnings;
use URI::Escape::XS qw/ uri_unescape /;
use Encode qw//;
use Encode::Guess qw/ eucjp shiftjis utf8 /;

my $out_enc  = Encode::find_encoding("eucjp");
my $in_encs  = {
    utf8     => Encode::find_encoding("utf8"),
    eucjp    => Encode::find_encoding("eucjp"),
    shiftjis => Encode::find_encoding("shiftjis"),
};

while ( my $line = <> ) {
    chomp $line;
    my @items = split " ", $line, 12;
    $items[10] = convert($items[10]) if $items[10] =~ /\?/;
    print join " ", @items;
    print "\n";
}

exit;

sub convert {
    my $referer = shift;
    $referer = uri_unescape($referer);
    return $referer if $referer =~ /^[\x00-\x7f]*$/sx; # is ascii

    my $in_enc =
        ( $referer =~ /=utf[-_]?8/isx )?       $in_encs->{utf8}
      : ( $referer =~ /=euc[-_]?jp/isx )?      $in_encs->{eucjp}
      : ( $referer =~ /=shift[-_]?jis/isx )?   $in_encs->{shiftjis}
      : ( $referer =~ /google\./isx )?         $in_encs->{utf8}
      :                                        Encode::Guess->guess($referer);

    return ref $in_enc?
        $out_enc->encode( $in_enc->decode($referer) )
      : $referer; # could not guess
}

__END__