Perl - кодировка файлов и сравнение слов - PullRequest
5 голосов
/ 05 мая 2011

У меня есть файл с одной фразой / термином в каждой строке, который я прочитал в perl из STDIN.У меня есть список стоп-слов (например, «á», «são», «é»), и я хочу сравнить каждое из них с каждым термином и удалить, если они равны.Проблема в том, что я не уверен в формате кодировки файла.

Я получаю это из команды file:

words.txt: Non-ISO extended-ASCII English text

Мой терминал Linux находится в UTF-8, и онпоказывает правильное содержание для некоторых слов, а для других нет.Вот вывод некоторых из них:

condi<E3>
conte<FA>dos
ajuda, mas não resolve
mo<E7>ambique
pedagógico são fenómenos

Вы можете видеть, что 3-я и 5-я строки правильно идентифицируют слова с акцентами и специальными символами, а другие - нет.Правильный вывод для других строк должен быть следующим: condiã, conteúdos и moçambique.

Если я использую binmode(STDOUT, utf8), «неправильные» строки теперь выводятся правильно, а другие нет.Например, 3-я строка:

Ajuda, Mas NÃ £ O решимости

Что я должен делать, ребята?

Ответы [ 2 ]

4 голосов
/ 05 мая 2011

Я настоятельно рекомендую вам создать фильтр, который берет файл со строками в смешанных кодировках и переводит их в чистый UTF-8. Тогда вместо

open(INPUT, "< badstuff.txt") || die "open failed: $!";

вы бы открыли либо фиксированную версию, либо канал из фиксатора, как:

open(INPUT, "fixit < badstuff.txt |") || die "open failed: $!"

В любом случае вы бы

binmode(INPUT, ":encoding(UTF-8)") || die "binmode failed";

Тогда программа fixit может просто сделать это:

use strict;
use warnings;
use Encode qw(decode FB_CROAK);

binmode(STDIN,  ":raw")  || die "can't binmode STDIN";
binmode(STDOUT, ":utf8") || die "can't binmode STDOUT";

while (my $line = <STDIN>) {
    $line = eval { decode("UTF-8", $line, FB_CROAK() };
    if ($@) { 
        $line = decode("CP1252", $line, FB_CROAK()); # no eval{}!
    }
    $line =~ s/\R\z/\n/;  # fix raw mode reads
    print STDOUT $line;    
}

close(STDIN)  || die "can't close STDIN: $!";
close(STDOUT) || die "can't close STDOUT: $!";
exit 0;

Видите, как это работает? Конечно, вы можете изменить его по умолчанию на другую кодировку или иметь несколько запасных вариантов. Вероятно, было бы лучше взять их список в @ARGV.

3 голосов
/ 05 мая 2011

Работает так:

C:\Dev\Perl :: chcp
Aktive Codepage: 1252.

C:\Dev\Perl :: type mixed-encoding.txt
eins zwei drei Käse vier fünf Wurst
eins zwei drei Käse vier fünf Wurst

C:\Dev\Perl :: perl mixed-encoding.pl < mixed-encoding.txt
eins zwei drei vier fünf
eins zwei drei vier fünf

Где mixed-encoding.pl выглядит так:

use strict;
use warnings;
use utf8; # source in UTF-8
use Encode 'decode_utf8';
use List::MoreUtils 'any';

my @stopwords = qw( Käse Wurst );

while ( <> ) { # read octets
    chomp;
    my @tokens;
    for ( split /\s+/ ) {
        # Try UTF-8 first. If that fails, assume legacy Latin-1.
        my $token = eval { decode_utf8 $_, Encode::FB_CROAK };
        $token = $_ if $@;
        push @tokens, $token unless any { $token eq $_ } @stopwords;
    }
    print "@tokens\n";
}

Обратите внимание, что скрипт не должен быть закодирован в UTF-8. Просто если в вашем скрипте есть данные с символами в стиле фанк, вы должны убедиться, что кодировка совпадает, поэтому use utf8, если ваша кодировка UTF-8, и нет, если это не так.

Обновление на основе звукового совета Триста:

use strict;
use warnings;
# source in Latin1
use Encode 'decode';
use List::MoreUtils 'any';

my @stopwords = qw( Käse Wurst );

while ( <> ) { # read octets
        chomp;
        my @tokens;
        for ( split /\s+/ ) {
                # Try UTF-8 first. If that fails, assume 8-bit encoding.
                my $token = eval { decode utf8 => $_, Encode::FB_CROAK };
                $token    = decode Windows1252 => $_, Encode::FB_CROAK if $@;
                push @tokens, uc $token unless any { $token eq $_ } @stopwords;
        }
        print "@tokens\n";
}
...