Вот _элементарный_ перекодировщик из win-1251 в koi-8r, написанный на Perl
Достаточно задать директорию и он перекодирует файлы опр. форматов из заданной директории и поддиректорий.
Перекодирует только _буквенные_ символы.
Работает медленно, потому что перекодирует файл _посимвольно_, а не построчно.
Код:

#!perl
# Вы задаете директорию. Скрипт перекодирует все файлы в этой директории из кодировки Win в KOI
#Замена текста происходит только в файлах с расширениями
#.pl, .txt, .htm , .html , .shtml, .asp, .php
# ВНИМАНИЕ: замена старого неперекодированного файла на новый происходит автоматически.
# Поэтому на случай неполадок стоит сделать бэкап директории
# Автор не несет никакой ответственности за действия произведенными скриптом
$msg="Директория, в которой перекодировать файлы (полный путь):";
wintodos($msg);
print $msg;
chomp($directory=<STDIN>);
@a = <$directory\*>;
&reading(@a);
sub reading {
my $i=0;
for ( ; $i < @_; $i++ )
{
if( &ifdir($_[$i]) !=0)
{
if($_[$i]=~/.pl|.txt|.htm|.asp|.php/){
XZxz (INPUT, "$_[$i]");
binmode(INPUT);
$newfile="$directory/temp.htm";
$msg="Сейчас перекодирую файл: $_[$i]\n";
wintodos($msg);
print $msg;
XZxz (FILEN, ">$newfile") || die "error filen:$!\n";
binmode(FILEN);
while(<INPUT>){
$tmp=wintokoi($_);
print FILEN $tmp;
}
close(INPUT);
close(FILEN);
rename ("$directory/temp.htm","$_[$i]");
}
}
}
}
sub ifdir()
{
if (opendir(DIR, "$_[0]"))
{
my @b=<$_[0]/*>;
&reading(@b);
return 0;
}
else
{
return 1;
}
}
sub wintodos {
$_[0]=~s/\xe0/\xa0/g;
$_[0]=~s/\xe1/\xa1/g;
$_[0]=~s/\xe2/\xa2/g;
$_[0]=~s/\xe3/\xa3/g;
$_[0]=~s/\xe4/\xa4/g;
$_[0]=~s/\xe5/\xa5/g;
$_[0]=~s/\xe6/\xa6/g;
$_[0]=~s/\xe7/\xa7/g;
$_[0]=~s/\xe8/\xa8/g;
$_[0]=~s/\xe9/\xa9/g;
$_[0]=~s/\xea/\xaa/g;
$_[0]=~s/\xeb/\xab/g;
$_[0]=~s/\xec/\xac/g;
$_[0]=~s/\xed/\xad/g;
$_[0]=~s/\xee/\xae/g;
$_[0]=~s/\xef/\xaf/g;
$_[0]=~s/\xf0/\xe0/g;
$_[0]=~s/\xf1/\xe1/g;
$_[0]=~s/\xf2/\xe2/g;
$_[0]=~s/\xf3/\xe3/g;
$_[0]=~s/\xf4/\xe4/g;
$_[0]=~s/\xf5/\xe5/g;
$_[0]=~s/\xf6/\xe6/g;
$_[0]=~s/\xf7/\xe7/g;
$_[0]=~s/\xf8/\xe8/g;
$_[0]=~s/\xf9/\xe9/g;
$_[0]=~s/\xfa/\xea/g;
$_[0]=~s/\xfb/\xeb/g;
$_[0]=~s/\xfc/\xec/g;
$_[0]=~s/\xfd/\xed/g;
$_[0]=~s/\xfe/\xee/g;
$_[0]=~s/\xff/\xef/g;
$_[0]=~s/\xc0/\x80/g;
$_[0]=~s/\xc1/\x81/g;
$_[0]=~s/\xc2/\x82/g;
$_[0]=~s/\xc3/\x83/g;
$_[0]=~s/\xc4/\x84/g;
$_[0]=~s/\xc5/\x85/g;
$_[0]=~s/\xc6/\x86/g;
$_[0]=~s/\xc7/\x87/g;
$_[0]=~s/\xc8/\x88/g;
$_[0]=~s/\xc9/\x89/g;
$_[0]=~s/\xca/\x8a/g;
$_[0]=~s/\xcb/\x8b/g;
$_[0]=~s/\xcc/\x8c/g;
$_[0]=~s/\xcd/\x8d/g;
$_[0]=~s/\xce/\x8e/g;
$_[0]=~s/\xcf/\x8f/g;
$_[0]=~s/\xd0/\x90/g;
$_[0]=~s/\xd1/\x91/g;
$_[0]=~s/\xd2/\x92/g;
$_[0]=~s/\xd3/\x93/g;
$_[0]=~s/\xd4/\x94/g;
$_[0]=~s/\xd5/\x95/g;
$_[0]=~s/\xd6/\x96/g;
$_[0]=~s/\xd7/\x97/g;
$_[0]=~s/\xd8/\x98/g;
$_[0]=~s/\xd9/\x99/g;
$_[0]=~s/\xda/\x9a/g;
$_[0]=~s/\xdb/\x9b/g;
$_[0]=~s/\xdc/\x9c/g;
$_[0]=~s/\xdd/\x9d/g;
$_[0]=~s/\xde/\x9e/g;
$_[0]=~s/\xdf/\x9f/g;
}
sub wintokoi {
$final="";
for ($i=0; $i<=length($_[0]);$i++){
$tmp=substr ($_[0], $i,1);
#этот участок нужно пройти один раз!!!
if($tmp=~s/\xc0/\xe1/g){}
elsif($tmp=~s/\xe1/\xc2/g){}
elsif($tmp=~s/\xc2/\xf7/g){}
elsif($tmp=~s/\xf7/\xde/g){}
elsif($tmp=~s/\xde/\xe0/g){}
elsif($tmp=~s/\xe0/\xc1/g){}
elsif($tmp=~s/\xc1/\xe2/g){}
elsif($tmp=~s/\xe2/\xd7/g){}
elsif($tmp=~s/\xd7/\xfe/g){}
elsif($tmp=~s/\xfe/\xc0/g){}
elsif($tmp=~s/\xd4/\xe6/g){}
elsif($tmp=~s/\xe6/\xd6/g){}
elsif($tmp=~s/\xd6/\xe3/g){}
elsif($tmp=~s/\xe3/\xc7/g){}
elsif($tmp=~s/\xc4/\xe4/g){}
elsif($tmp=~s/\xe4/\xc4/g){}
elsif($tmp=~s/\xc5/\xe5/g){}
elsif($tmp=~s/\xe5/\xc5/g){}
elsif($tmp=~s/\xc3/\xe7/g){}
elsif($tmp=~s/\xe7/\xda/g){}
elsif($tmp=~s/\xda/\xff/g){}
elsif($tmp=~s/\xff/\xd1/g){}
elsif($tmp=~s/\xd1/\xf3/g){}
elsif($tmp=~s/\xf3/\xd5/g){}
elsif($tmp=~s/\xd5/\xe8/g){}
elsif($tmp=~s/\xe8/\xc9/g){}
elsif($tmp=~s/\xd3/\xf5/g){}
elsif($tmp=~s/\xf5/\xc8/g){}
elsif($tmp=~s/\xc8/\xe9/g){}
elsif($tmp=~s/\xe9/\xca/g){}
elsif($tmp=~s/\xc9/\xea/g){}
elsif($tmp=~s/\xea/\xcb/g){}
elsif($tmp=~s/\xca/\xeb/g){}
elsif($tmp=~s/\xeb/\xcc/g){}
elsif($tmp=~s/\xcb/\xec/g){}
elsif($tmp=~s/\xec/\xcd/g){}
elsif($tmp=~s/\xcc/\xed/g){}
elsif($tmp=~s/\xed/\xce/g){}
elsif($tmp=~s/\xcd/\xee/g){}
elsif($tmp=~s/\xee/\xcf/g){}
elsif($tmp=~s/\xce/\xef/g){}
elsif($tmp=~s/\xef/\xd0/g){}
elsif($tmp=~s/\xfa/\xdf/g){}
elsif($tmp=~s/\xcf/\xf0/g){}
elsif($tmp=~s/\xf0/\xd2/g){}
elsif($tmp=~s/\xdf/\xf1/g){}
elsif($tmp=~s/\xf1/\xd3/g){}
elsif($tmp=~s/\xd0/\xf2/g){}
elsif($tmp=~s/\xf2/\xd4/g){}
elsif($tmp=~s/\xd2/\xf4/g){}
elsif($tmp=~s/\xf4/\xc6/g){}
elsif($tmp=~s/\xc6/\xf6/g){}
elsif($tmp=~s/\xf6/\xc3/g){}
elsif($tmp=~s/\xdc/\xf8/g){}
elsif($tmp=~s/\xf8/\xdb/g){}
elsif($tmp=~s/\xdb/\xf9/g){}
elsif($tmp=~s/\xf9/\xdd/g){}
elsif($tmp=~s/\xc7/\xfa/g){}
elsif($tmp=~s/\xf1/\xdf/g){}
elsif($tmp=~s/\xd8/\xfb/g){}
elsif($tmp=~s/\xfb/\xd9/g){}
elsif($tmp=~s/\xdd/\xfc/g){}
elsif($tmp=~s/\xfc/\xd8/g){}
elsif($tmp=~s/\xd9/\xfd/g){}
elsif($tmp=~s/\xfd/\xdc/g){}
elsif($tmp=~s/\xb8/\xc5/g){}
$final=$final.$tmp;
}
return $final;
}