2015-08-11 9 views
6

Sto scrivendo un programma Perl per convertire i caratteri ASCII della mia lingua locale in caratteri Unicode (Tamil).Stampa su un file o stampa su shell in Perl

Questo è il mio programma di

#!/bin/perl 
use strict; 
use warnings; 

use open ':std'; 
use open ':encoding(UTF-8)'; 

use Encode qw(encode decode); 
use Data::Dump qw(dump); 
use Getopt::Long qw(GetOptions); 

Getopt::Long::Configure qw(gnu_getopt); 

my $font; 
my %map; 
GetOptions(
    'font|f=s' => \$font, 
    'help|h' => \&usage, 
) or die "Try $0 -h for help"; 

print "Do you want to map $font? (y/n)"; 
chomp(my $answer = lc <STDIN>); 

$font = lc($font); 
$font =~ s/ /_/; 
$font =~ s/(.*?)\.ttf/$1/; 

if ($answer eq "y") { 
    map_font(); 
} 
else { 
    restore_map(); 
} 

foreach (@ARGV) { 

    my $modfile = "$_"; 

    $modfile =~ s/.*\/(.*)/uni$1/; 

    process_file($_, $modfile); 
} 

sub process_file { 

    my @options = @_; 

    open my $source, '<', "$options[0]"; 
    my $result = $options[1]; 
    my $test = "./text"; 
    my $missingchar = join("|", map(quotemeta, sort { length $b <=> length $a } keys %map)); 

    while (<$source>) { 
     $/ = undef; 
     s/h;/u;/g;  #Might need change based on the tamil font 
     s/N(.)/$1N/g; #Might need change based on the tamil font 
     s/n(.)/$1n/g; #Might need change based on the font 
     s/($missingchar)/$map{$1}/g; 

     print "$_"; 

     open my $final, '>:utf8', "$result"; 
     print $final "$_"; 
     close $final; 
    } 
} 

sub map_font { 

    my @oddhexes = qw/0B95 0B99 0B9A 0B9E 0B9F 0BA3 0BA4 0BA8 0BAA 0BAE 0BAF 0BB0 0BB2 0BB5 0BB3 0BB4 0BB1 0BA9/; 
    my @missingletters = qw/0BC1 0BC2/; 
    my @rest = qw/0B85 0B86 0B87 0B88 0B89 0B8A 0B8E 0B8F 0B90 0B92 0B93 0B83 0BBE 0BBF 0BC0 0BC6 0BC7 0BC8 0BCD 0B9C 0BB7 0BB8 0BB9 0BCB 0BCA 0BCC/; 

    foreach (@oddhexes) { 

     my $oddhex = $_; 

     $_ = encode('utf8', chr(hex($_))); 
     print "Press the key for $_ :"; 
     chomp(my $bole = <STDIN>); 
     if ($bole eq "") { 
      next; 
     } 

     $map{$bole} = $_; 

     foreach (@missingletters) { 

      my $oddchar = encode('utf8', chr(hex($oddhex)) . chr(hex($_))); 

      print "Press the key for $oddchar :"; 
      chomp(my $missingchar = <STDIN>); 
      if ($missingchar eq "") { 
       next 
      } 

      $map{$missingchar} = $oddchar; 
     } 

    } 

    foreach (@rest) { 

     $_ = encode('utf8', chr(hex($_))); 

     print "Press the key for $_ :"; 
     chomp(my $misc = <STDIN>); 
     if ($misc eq "") { 
      next 
     } 

     $map{$misc} = $_; 
    } 

    open my $OUTPUT, '>', $font || die "can't open file"; 
    print $OUTPUT dump(\%map); 
    close $OUTPUT; 
} 

sub restore_map { 

    open my $in, '<', "$font" || die "can't open file: $!"; 

    { 
     local $/; 
     %map = %{ eval <$in> }; 
    } 

    close $in; 
} 

sub usage { 
    print "\nUsage: $0 [options] {file1.txt file2.txt..} \neg: $0 -f TamilBible.ttf chapter.txt\n\nOptions:\n -f --font - used to pass font name\n -h --help - Prints help\n\nManual mapping of font is essential for using this program\n"; 
    exit; 
} 

In subroutine process_file, uscita di print "$_"; schermi adeguati caratteri Unicode Tamil nel terminal.

Tuttavia il output to the file handle$final è molto diverso.

Il %map è here.

Perché le uscite sono diverse?

Come posso correggere questo comportamento?

Ho visto questo question ma questo non è lo stesso. Nel mio caso il terminale visualizza correttamente il risultato mentre l'output del filehandle è diverso.

+0

Si dovrebbe considera l'uso di * modificatori di istruzioni * come 'next if if $ misc eq" "' invece di 'if ($ misc eq" ") {next}' – Borodin

+0

@Borodin Grazie per il suggerimento. Modificherò il codice –

+0

ASCII non significa quello che pensi che significhi. –

risposta

9

La sua dichiarazione aperta

open my $final, '>:utf8', "$result"; 

imposta il tuo file handle aspettarsi personaggi, e per codificare in UTF-8 sequenze poi sulla via d'uscita. Ma si sta inviando è pre-codificato sequenze di byte dal %map hash, che fa sì che quei byte per essere trattati come carattere e codificati nuovo da Perl IO

Al contrario, il terminale è impostato su aspettarsi UTF-8-encoded dati, ma STDOUT non è impostato per eseguire qualsiasi codifica affatto (use open ':std' non ha effetto sulla propria, vedi sotto) quindi passa tuoi byte UTF-8-encoded attraverso invariato che risulta essere quello che il terminale si aspetta

A proposito, è stata impostata una modalità di apertura predefinita di :encoding(UTF-8) per flussi di input e output con

use open ':encoding(UTF-8)' 

ma hanno sovrascritto la chiamata a open. La modalità :utf8 esegue una traduzione di base da caratteri ampi a sequenze di byte, ma lo :encoding(UTF-8) è molto più utile perché controlla che ogni carattere stampato sia un valore Unicode valido. C'è una buona probabilità che si sarebbe preso un errore come questo, e sarebbe stato meglio per consentire il default e scrivere solo

open my $final, '>', $result; 

Per mantenere le cose pulite e ordinate, il programma dovrebbe funzionare in caratteri , e le maniglie di file devono essere impostati per codificare i caratteri UTF-8 quando tali caratteri vengono stampati

È possibile impostare UTF-8 come codifica predefinita per tutti i file di recente apertura maniglie così come STDIN e STDOUT con l'aggiunta di

use open qw/ :std :encoding(utf-8) /; 

alla parte superiore del vostro programma (:encoding(utf-8) è preferibile :utf8) e rimuovere tutti i chiamate a encode.Avevi quasi a destra, ma la necessità :std e :encoding(utf-8) di essere nella stessa use dichiarazione

Si dovrebbe anche aggiungere

use utf8; 

in cima in modo che è possibile utilizzare caratteri UTF-8 nel programma stesso

Hai anche alcuni errori casuali. Per esempio

  • Nella dichiarazione

    open my $in, '<', "$font" || die "can't open file: $!"; 
    

    è quasi sempre sbagliato citare un singolo variabile scalare come $font a meno che non capita di essere un oggetto e si desidera richiamare il metodo in stringa

    Hai bisogno di or invece di ||, altrimenti stai solo testando la verità di $font

    Se ti chiedessi cosa potrebbe contenere una variabile denominata $in, credo che potresti essere titubante; $in_fh è meglio ed è un idioma comune

    E 'sempre bello mettere il nome del file nella stringa die così come la ragione da $!

    Prendendo tutti coloro in considerazione fa la sua dichiarazione simile a questa

    open my $in_fh, '<', $font or die qq{Unable to open "$font" for input: $!}; 
    
  • si dovrebbe essere coerente tra le variabili scalari caso superiore e inferiore, e minuscolo è la scelta giusta. Così

    open my $OUTPUT, '>', $font || die "can't open file"; 
    

    dovrebbe essere qualcosa di simile a

    open my $out_fh, '>', $font or die qq{Unable to open "$font" for output: $!}; 
    
  • La linea

    $/ = undef; 
    

    dovrebbe essere local $/ come si è utilizzato altrove, altrimenti si è in modo permanente modificando il separatore di record in ingresso per il resto del tuo programma e dei tuoi moduli. Appare anche dopo la prima lettura dal file handle, in modo che il programma verrà leggere ed elaborare una linea, e poi tutto il resto del file nella prossima iterazione del ciclo while

+0

Grazie mille !! Hai sottolineato tutte le altre cose di cui mi stavo chiedendo anch'io. –

+1

@OneFace: fantastico. Sei riuscito a farlo funzionare senza le chiamate 'encode'? – Borodin

+0

Sto ricontrollando il codice adesso.Devo ri-mappare il font in quanto i valori dei caratteri sono molto diversi quando si usa ': std: encoding (utf-8)'. Sto ancora ingrandendo il programma aggiungendo varie opzioni. Pubblicherò il risultato una volta re-mappato. In questo momento è ora di andare a letto. Grazie ancora per l'aiuto estremamente prezioso! –