Ficheros Unicode

Operadores, STDOUT y Unicode

Considere el siguiente programa:

lhp@nereida:~/Lperl/src/testing$ cat -n useutf8_1.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3
     4  my $x = 'áéíóúñ€';
     5  print "$x\n";
     6  print length($x)."\n";
Cuando lo ejecutamos obtenemos la salida:
lhp@nereida:~/Lperl/src/testing$ useutf8_1.pl
áéíóúñ€
15
Perl tiene dos modos de procesamiento de datos: el modo byte y el modo carácter. El modo por defecto es el modo byte. Este modo es conveniente cuando se trabaja con ficheros binarios (p. ej. una imagen JPEG) y con texto codificado con un código que requiere un sólo byte por carácter como es el caso de Latin 1.

En efecto, la cadena 'áéíóúñ€' - que es una cadena unicode codificada en UTF-8 - tiene una longitud de 15 bytes. El asunto es que no es lo mismo la longitud en bytes que la longitud en caracteres cuando nos salimos de ASCII y Latin1. Si queremos que length devuelva la longitud en caracteres usemos utf8:

lhp@nereida:~/Lperl/src/testing$ cat -n useutf8_2.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use utf8;
     4
     5  my $x = 'áéíóúñ€';
     6  print "$x\n";
     7  print length($x)."\n";
Al ejecutar obtenemos la longitud en caracteres:
lhp@nereida:~/Lperl/src/testing$ useutf8_2.pl
Wide character in print at ./useutf8_2.pl line 6.
áéíóúñ€
7
Ahora length retorna la longitud en caracteres.

Obsévese el mensaje de advertencia. Si queremos asegurar el buen funcionamiento de la salida por STDOUT con caracteres codificados en UTF-8 debemos llamar a binmode sobre STDOUT con la capa ':utf8':

lhp@nereida:~/Lperl/src/testing$ cat -n useutf8_3.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use utf8;
     4  binmode(STDOUT, ':utf8');
     5
     6  my $x = 'áéíóúñ€';
     7  print "$x\n";
     8  print length($x)."\n";
El mensaje de advertencia desaparece:
lhp@nereida:~/Lperl/src/testing$ useutf8_3.pl
áéíóúñ€
7
Usando la opción -C del intérprete Perl se puede conseguir el mismo resultado:

lhp@nereida:~/Lperl/src/testing$ perl useutf8_1.pl
áéíóúñ€
15
lhp@nereida:~/Lperl/src/testing$ perl -Mutf8 -COE useutf8_1.pl
áéíóúñ€
7

Lea perldoc perlrun para mas información sobre estas opciones:

As of 5.8.1, the "-C" can be followed either by a number or a list of option
letters.  The letters, their numeric values, and effects are as follows;
listing the letters is equal to summing the numbers.

  I     1    STDIN is assumed to be in UTF-8
  O     2    STDOUT will be in UTF-8
  E     4    STDERR will be in UTF-8
  S     7    I + O + E
  i     8    UTF-8 is the default PerlIO layer for input streams
  o    16    UTF-8 is the default PerlIO layer for output streams
  D    24    i + o
  A    32    the @ARGV elements are expected to be strings encoded in UTF-8
  L    64    normally the "IOEioA" are unconditional,
             the L makes them conditional on the locale environment
             variables (the LC_ALL, LC_TYPE, and LANG, in the order
             of decreasing precedence) -- if the variables indicate
             UTF-8, then the selected "IOEioA" are in effect

Ficheros Unicode en vim

Hay varias formas de crear ficheros Unicode en lenguajes fuera del rango del latin1 con vim.

Los caracteres unicode en la línea 3 del siguiente fichero han sido generados en vim insertandolos mediante su codificación usando la secuencia CTRL-V u hexcode.

lhp@nereida:~/Lperl/src/testing$ cat -n utf8file.txt
     1  áéíóúñÑ
     2  àèìòùÇç
     3  ェッニは大き
En concreto los códigos creo que fueron: 30a7 30c3 30cb 306f 5927 304d.
$ perl -Mutf8 -we \
  'binmode(STDOUT, ":utf8"); \
   print chr($_)."\n" for (0x30a7, 0x30c3, 0x30cb, 0x306f,  0x5927, 0x304d)'
ェ
ッ
ニ
は
大
き
Una forma mas cómoda de insertar caracteres Unicode en vim es usar keymaps:
  1. Vea que keymaps están disponibles ejecutando el comando vim:

    :echo globpath(&rtp, "keymap/*.vim")
    
    Esto mostrará algo como:
    /usr/share/vim/vim70/keymap/accents.vim
    /usr/share/vim/vim70/keymap/arabic.vim
    /usr/share/vim/vim70/keymap/arabic_utf-8.vim
    /usr/share/vim/vim70/keymap/bulgarian.vim
    /usr/share/vim/vim70/keymap/canfr-win.vim
    /usr/share/vim/vim70/keymap/czech.vim
    /usr/share/vim/vim70/keymap/czech_utf-8.vim
    /usr/share/vim/vim70/keymap/esperanto.vim
    /usr/share/vim/vim70/keymap/esperanto_utf-8.vim
    /usr/share/vim/vim70/keymap/greek.vim
    /usr/share/vim/vim70/keymap/greek_cp1253.vim
    /usr/share/vim/vim70/keymap/greek_cp737.vim
    /usr/share/vim/vim70/keymap/greek_iso-8859-7.vim
    /usr/share/vim/vim70/keymap/greek_utf-8.vim
    ....
    
    Como se ve el convenio de nombres para los keymaps es:
    <language>_<encoding>.vim
    
    Sigue un ejemplo de fichero de keymap:
    $ cat -n /usr/share/vim/vim70/keymap/greek_utf-8.vim
     1 " Vim Keymap file for greek
     2 " Maintainer: Panagiotis Louridas <louridas@acm.org>
     3 " Last Updated: Thu Mar 23 23:45:02 EET 2006
     4 
     .......................................................................
    72 let b:keymap_name = "grk"
    73 loadkeymap
    74 " PUNCTUATION MARKS - SYMBOLS (GREEK SPECIFIC)
    75 "
    76 E$	<char-0x20AC>  " EURO SIGN
    ............................................................................
    115 "
    116 " GREEK LETTERS
    117 "
    118 A	<char-0x0391>   " GREEK CAPITAL LETTER ALPHA
    119 B	<char-0x0392>   " GREEK CAPITAL LETTER BETA
    120 G	<char-0x0393>   " GREEK CAPITAL LETTER GAMMA
    121 D	<char-0x0394>   " GREEK CAPITAL LETTER DELTA
    122 E	<char-0x0395>   " GREEK CAPITAL LETTER EPSILON
    123 Z	<char-0x0396>   " GREEK CAPITAL LETTER ZETA
    
  2. Ahora ejecute el comando:
    :set keymap=greek
    
    Cuando estamos en modo inserción podemos conmutar entre los dos keymaps tecleando

    CTRL-^.
    
    o bien
    CTRL-6.
    
  3. Compruebe con que codificación está trabjando vim:
    :set encoding
      encoding=utf-8
    
    Es posible cambiar la codificación con la que se está editando:
    :set encoding latin1
    
    Esto no modifica la codificación del fichero.
  4. Para saber mas sobre como utilizar Unicode y keymaps en vim lea las ayudas sobre los tópicos:
    :help mbyte.txt                        
    :help mbyte-keymap
    

Apertura de ficheros UTF-8

Use la forma con tres argumentos de open y especifique la capa :utf8 para que la entrada/salida a ese fichero se procesada por dicha capa. Por ejemplo:

lhp@nereida:~/Lperl/src/testing$ cat -n abreutf8.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  binmode(STDOUT, "utf8");
 4  open my $f, '<:utf8', shift();
 5  my @a = <$f>;
 6  chomp(@a);
 7  print "$_ tiene longitud ".length($_)."\n" for @a;

Al ejecutar produce una salida como esta:

lhp@nereida:~/Lperl/src/testing$ abreutf8.pl tutu
ジジェッニgfは大好あき tiene longitud 14
αβγεφγη tiene longitud 7
νμοπ;ρ^αβψδε tiene longitud 12
& ασηφδξδξδη tiene longitud 12
abc tiene longitud 3
αβγδ&αβψ tiene longitud 8

El Módulo charnames

El módulo charnames facilita la introducción de caracteres unicode:

lhp@nereida:~/Lperl/src/testing$ cat -n alfabeta.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use charnames qw{:full greek hebrew katakana};
 4  binmode(STDOUT, ':utf8');
 5
 6  print "\N{alpha}+\N{beta} = \N{pi}\n";
 7  print "\N{alef} es la primera letra del alfabeto hebreo\n";
 8  print "Un poco de Katakana: \N{sa}\N{i}\N{n}\N{mo}\n";
 9
10  # Usando el nombre completo definido en el Standard Unicode
11  print "Hello \N{WHITE SMILING FACE}\n";
Cuando se ejecuta produce una salida como:
lhp@nereida:~/Lperl/src/testing$ alfabeta.pl
α+β = ピ
א es la primera letra del alfabeto hebreo
Un poco de Katakana: サインモ
Hello ☺
Obsérvese como la salida para \N{pi} no muestra la letra griega π sino el correspondiente símbolo Katakana : atención a las colisiones entre alfabetos.

Expresiones Regulares y Unicode

Usando utf8 es posible usar operadores como tr y expresiones regulares sobre cadenas UTF-8:

lhp@nereida:~/Lperl/src/testing$ cat -n useutf8.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use utf8;
 4  binmode(STDOUT, ':utf8');
 5
 6  my $x = 'áéíóúñ€';
 7  print "$x\n";
 8  print length($x)."\n";
 9
10  my$y = $x;
11  $y =~ tr/áéíóúñ€/aeioun$/;
12  print "$y\n";
13
14  $y = $x;
15  $y =~ m/áéíóúñ(€)/;
16  print "$1\n";
Al ejecutar, este programa produce la salida:
lhp@nereida:~/Lperl/src/testing$ useutf8.pl
áéíóúñ€
7
aeioun$
€

Macros: Dígitos y Words

Macros como \d han sido generalizadas. Los digitos Devanagari tienen códigos del 2406 (0x966) al 2415 (0x96F):

lhp@nereida:~/Lperl/src/testing$ unicode -x 966..96f | egrep '096|\.0'
          .0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .A .B .C .D .E .F
     096.  ॠ  ॡ      ।  ॥  ०  १  २  ३  ४  ५  ६  ७  ८  ९
El siguiente ejemplo muestra que expresiones regulares como \d+ reconocen los digitos Devanagari:
lhp@nereida:~/Lperl/src/testing$ cat -n regexputf8.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  binmode(STDOUT, "utf8");
 4  use utf8;
 5
 6  # Digitos Devanagari del 0 al 9
 7  my @dd  = map { chr } (2406..2415);
 8  my $x = join '+', @dd;
 9  print "La interpolación ocurre: $x\n";
10  my @number = $x =~ m{(\d+)}g;
11  print "Las expresiones regulares funcionan: @number\n";
12  print "Sin embargo la conversión numérica no es automática: ".($number[0]+$number[1])."\n";

Como se indica en el programa la conversión automática de dígitos en otros juegos de caracteres no funciona. Véase la ejecución:

lhp@nereida:~/Lperl/src/testing$ regexputf8.pl
La interpolación ocurre: ०+१+२+३+४+५+६+७+८+९
Las expresiones regulares funcionan: ० १ २ ३ ४ ५ ६ ७ ८ ९
Argument "\x{967}" isn't numeric in addition (+) at ./regexputf8.pl line 12.
Argument "\x{966}" isn't numeric in addition (+) at ./regexputf8.pl line 12.
Sin embargo la conversión numérica no es automática: 0

Lo mismo ocurre con la macro\w:

lhp@nereida:~/Lperl/src/testing$ cat -n words_utf8.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use utf8;
     4  use charnames qw{greek};
     5  binmode(STDOUT, ':utf8');
     6
     7  my $x = 'áéíóúñ€αβγδη';
     8  my @w = $x =~ /(\w)/g;
     9  print "@w\n";
lhp@nereida:~/Lperl/src/testing$ words_utf8.pl
á é í ó ú ñ α β γ δ η

Semántica de Carácter versus Semántica de Byte

Cuando se procesan datos codificados en UTF-8 el punto casa con un carácter UTF-8. La macro \C puede ser utilizada para casar un byte:

lhp@nereida:~/Lperl/src/testing$ cat -n dot_utf8.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use utf8;
     4  use charnames qw{greek};
     5
     6  binmode(STDOUT, ':utf8');
     7
     8  my $x = 'αβγδεφ';
     9
    10  my @w = $x =~ /(.)/g;
    11  print "@w\n";
    12
    13  my @v = map { ord } $x =~ /(\C)/g;
    14  print "@v\n";
lhp@nereida:~/Lperl/src/testing$ dot_utf8.pl
α β γ δ ε φ
206 177 206 178 206 179 206 180 206 181 207 134

El mismo efecto de \C puede lograrse mediante el pragma use bytes el cual cambia la semántica de caracteres a bytes:

lhp@nereida:~/Lperl/src/testing$ cat -n dot_utf8_2.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use utf8;
     4  use charnames qw{greek};
     5
     6  binmode(STDOUT, ':utf8');
     7
     8  my $x = 'αβγδεφ';
     9
    10  my @w = $x =~ /(.)/g;
    11  print "@w\n";
    12
    13  {
    14    use bytes;
    15    my @v = map { ord } $x =~ /(.)/g;
    16    print "@v\n";
    17  }

Caja e Inversión de Cadenas Unicode

El siguiente ejemplo ilustra el uso de las funciones de cambio de caja (tales como uc, lc, lcfirst y ucfirst) asi como el uso de reverse con cadenas unicode:

lhp@nereida:~/Lperl/src/testing$ cat -n alfabeta1.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use utf8;
 4  use charnames qw{greek};
 5  binmode(STDOUT, ':utf8');
 6
 7  my $x = "\N{alpha}+\N{beta} = \N{pi}";
 8  print uc($x)."\n";
 9  print scalar(reverse($x))."\n";
10
11  my $y = "áéíóúñ";
12  print uc($y)."\n";
13  print scalar(reverse($y))."\n";
Al ejecutarse, el programa produce la salida:

lhp@nereida:~/Lperl/src/testing$ alfabeta1.pl
Α+Β = Π
π = β+α
ÁÉÍÓÚÑ
ñúóíéá

Propiedades

El estandar Unicode declara que cadenas particulares de caracteres pueden tener propiedades particulares y que una expresión regular puede casar sobre esas propiedades utilizando la notación \p{...}:

lhp@nereida:~/Lperl/src/testing$ cat -n properties.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use utf8;
 4  use charnames qw{greek};
 5  binmode(STDOUT, ':utf8');
 6
 7  my @a = ('$', 'az', '£', 'α', '€', '¥');
 8  my $x =  "@a\n";
 9
10  print /\p{CurrencySymbol}/? "$_ = Dinero!!\n" : "$_ : No hay dinero\n" for @a;
11  print /\p{Greek}/? "$_ = Griego\n" : "$_ : No es griego\n" for @a;
Al ejecutar este script obtenemos:

lhp@nereida:~/Lperl/src/testing$ properties.pl
$ = Dinero!!
az : No hay dinero
£ = Dinero!!
α : No hay dinero
€ = Dinero!!
¥ = Dinero!!
$ : No es griego
az : No es griego
£ : No es griego
α = Griego
€ : No es griego
¥ : No es griego

Conversores

Hay un buen número de utilidades de conversión

Véase también



Subsecciones
Casiano Rodríguez León
2009-10-04