• Publicidad

use UTF8, utf8 NO BOM y librerías

Todo lo relacionado con el desarrollo Web con Perl: desde CGI hasta Mojolicious

Re: use UTF8, utf8 NO BOM y librerías

Notapor explorer » 2013-08-26 10:05 @462

En el documento perldoc perlhist está registrada la fecha de salida de todas las versiones:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
        Rafael   5.10.0        2007-Dec-18

        David M  5.10.1-RC1    2009-Aug-06     The 5.10 maintenance track
                 5.10.1-RC2    2009-Aug-18
                 5.10.1        2009-Aug-22
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4
La v5.10.0 tenía algunos problemas graves. Lo recomendable hoy en día es usar como mínimo la v5.10.1. Pero es del año 2009 :)

Yo suelo usar de forma diaria las versiones v5.12, v5.14 y v5.16.

Lo que te pasa es muy extraño... Por alguna razón tienes instalada una versión v5.18, pero no corresponde con el resto de Perl del sistema.

Puedes comprobar qué versión de Perl estás usando haciendo un perl -v (más información con un perl -V).

Es posible que le tengas que pedir al gestor de paquetes de software de tu sistema operativo que reinstale todo un perl completo.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Publicidad

Re: use UTF8, utf8 NO BOM y librerías

Notapor lr_emilio » 2013-09-16 10:59 @499

Hola,

sigo con mis pequeños problemas de encoding. Al final puse la versión 5.10.1 que es la que trae por defecto CentOS 6.4.

Tengo un problema un tanto curioso que detallo a continuación:

Cuando envío una variable a través de la función Ajax de jQuery al archivo .pl, la recojo en una variable, por ejemplo, $NombreVariable.

Si hago un print $NombreVariable para que en la función success de Ajax me haga un alert se me muestran los caracteres raros del tipo ñññ.

En cambio, si $NombreVariable lo guardo en la base de datos, y luego realizo una búsqueda del campo y lo guardo en una variable e imprimo dicha variable como antes, ahora sí me muestra bien los caracteres.

¿Qué puede estar pasando?

¡Muchas gracias y un saludo!
lr_emilio
Perlero nuevo
Perlero nuevo
 
Mensajes: 25
Registrado: 2013-08-01 11:57 @539

Re: use UTF8, utf8 NO BOM y librerías

Notapor explorer » 2013-09-16 11:21 @514

Esos caracteres raros indican que has recibido información codificada, muy posiblemente en utf-8.

El hacer el paso por la base de datos, se explica porque algunos motores de bases de datos hacen una de/codificación automática de los datos.

Necesitas saber:
  • en qué codificación estás recibiendo los datos. Si es una página web, lo normal es que sea la codificación en la que se encuentra la página
  • en qué codificación sacas los datos con el print. Por defecto, Perl siempre trabaja con iso-8859-1
  • si los datos que sacas con el print se dirigen a una terminal, en qué codificación está trabajando esa terminal
  • si estás trabajando con una base de datos, tendrás que investigar si realiza una de/codificación automática
En el foro avanzado hay un hilo dedicado específicamente al tema del Unicode-utf8 con Perl.

El resumen es: «decodifica lo que te llega, codifica lo que mandes».
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: use UTF8, utf8 NO BOM y librerías

Notapor lr_emilio » 2013-09-16 11:28 @519

Hola explorer, te respondo:

explorer escribiste:Necesitas saber:
  • en qué codificación estás recibiendo los datos. los datos vienen de la página web en UTF-8. Ahora bien... no sé si perl los está leyendo correctamente en utf8 o ya en iso.
  • en qué codificación sacas los datos con el print. Por defecto, Perl siempre trabaja con iso-8859-1. me los saca en ISO-8859-1 cosa que no quiero. Quiero que salgan siempre en utf8.
  • si los datos que sacas con el print se dirigen a una terminal, en qué codificación está trabajando esa terminal. Los saco al navegador cuya página está en utf8
  • si estás trabajando con una base de datos, tendrás que investigar si realiza una de/codificación automática


en la cabecera del .pl tengo esto:

use DBI;
use Encode;
use utf8;
use JSON;
use open ':encoding(utf8)';
binmode(STDOUT, ":utf8");
lr_emilio
Perlero nuevo
Perlero nuevo
 
Mensajes: 25
Registrado: 2013-08-01 11:57 @539

Re: use UTF8, utf8 NO BOM y librerías

Notapor explorer » 2013-09-16 14:27 @644

lr_emilio escribiste:en qué codificación estás recibiendo los datos. los datos vienen de la página web en UTF-8. Ahora bien... no sé si perl los está leyendo correctamente en utf8 o ya en iso.
El tema está en la expresión "leer". Si con "leer" te refieres a leer los valores que te llegan desde la entrada estándar (la petición que llega por el Ajax), entonces lo más cómodo es decirle a Perl que trate la entrada estándar como que esté en utf8. Luego te digo cómo.

lr_emilio escribiste:en qué codificación sacas los datos con el print. Por defecto, Perl siempre trabaja con iso-8859-1. me los saca en ISO-8859-1 cosa que no quiero. Quiero que salgan siempre en utf8.
Bueno, con la opción binmode() que pones en tu código, te vale, si los diriges hacia la salida estándar (STDOUT).

lr_emilio escribiste:si los datos que sacas con el print se dirigen a una terminal, en qué codificación está trabajando esa terminal. Los saco al navegador cuya página está en utf8
Supongo que enviando la salida haciendo print hacia la salida estándar. Entonces con el binmode(), sirve. O usar el pragma open como te digo a continuación.

El programa quedaría así, al principio:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use DBI;                    # necesario para hablar con la base de datos
  2. use Encode;                 # necesario por si tenemos que de/codificar datos que leamos de la base de datos o archivos
  3. use utf8;                   # necesario SOLO si hay caracteres utf8 en ESTE código (este programa tiene literales en utf8)
  4. use JSON;                   # necesario, claro
  5. #use open ':utf8';          # aquí decimos que todos los archivos abiertos con open(), serán de/codificados en utf8
  6. #use open ':std';           # aquí decimos que STDIN, STDOUT y STDERR estarán en la codificación indicada antes (utf8)
  7. use open qw(:std :utf8);    # esta línea hace lo misma que las dos anteriores
  8. #binmode(STDOUT, ":utf8");  # esta línea ya no es necesaria, por efecto de la línea anterior
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4

Sacado de la receta ℞ 15: Declarar STD{IN,OUT,ERR} para que sean UTF-8.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: use UTF8, utf8 NO BOM y librerías

Notapor lr_emilio » 2013-09-17 09:31 @438

Hola, he probado todo lo que me has dicho y no consigo que funcione.

he hecho este sencillo html que falla para ver qué puede estar pasando.

Todo lo que saca el cgi viene con caracteres raros hacia la web...
Sintáxis: [ Descargar ] [ Ocultar ]
Using html4strict Syntax Highlighting
  1. <!DOCTYPE HTML>
  2. <html lang = "es">
  3.   <head>
  4.     <meta charset = "UTF-8" />
  5.     <title>
  6.       Prueba UTF8
  7.     </title>
  8.     <script type="text/javascript" language="javascript" src="ficheros/jquery-ui-1.10.1.custom/js/jquery-1.9.1.js"></script>
  9.     <script>
  10.         $(document).ready(function(){
  11.                 $("#conajax").click(function(e){
  12.                         e.preventDefault();
  13.                         $.ajax({
  14.                             url: "cgi-bin/pruebautf8.pl?A2",
  15.                             cache: false,
  16.                             async: false,
  17.                             type: "POST",
  18.                             dataType: "html",
  19.                             data: {cadena:$("#cadena").val()},
  20.                             success: function(data) {
  21.                                                 alert(data);
  22.                             }
  23.                 });
  24.                        
  25.                 });    
  26.                
  27.         });    
  28.     </script>
  29.   </head>
  30.   <body>
  31.     <h1>
  32.       Pruebas UTF8 para guardar en la base con ajax y sin ajax.
  33.     </h1>
  34.     <FORM id="F0" name="F0" method="POST" action="cgi-bin/pruebautf8.pl?A1">
  35.         <input type="text" name="cadena" id="cadena"    value=""/><label>Escribir carácteres para guardar en la base</label><br> <br>
  36.         <input type="submit" value="Guardar sin ajax" />       
  37.     </FORM>
  38.     <br> <br>
  39.     <a href="#" id="conajax">Guardar con ajax</a>
  40.    
  41.    
  42.   </body>
  43. </html>
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

y este es el .pl
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.  
  2. use Encode;
  3. use utf8;
  4. &Parametros1;
  5.  
  6.  
  7. if    ($Marca eq "A1")     {&AlmacenaCaracteressinajax;}
  8. elsif ($Marca eq "A2")    {&AlmacenaCaracteresconajax}
  9.  
  10. else        {print "Content-type: text/html\n\n";print "Error";}
  11. #########################
  12.  
  13.  
  14. #Marca A1
  15. sub AlmacenaCaracteressinajax{
  16.         $Cadena = $FORM{'cadena'};
  17. #       $datos="'','$Cadena','A','N'";
  18. #  &IncDaTb("PruebasUTF8","$datos");   
  19.         binmode STDOUT, ":utf8";
  20.         print "Content-type: text/html\n\n";
  21.        
  22.         print $Cadena;
  23. }
  24.  
  25.  
  26. #Marca A2
  27. sub AlmacenaCaracteresconajax{
  28.         $Cadena = $FORM{'cadena'};
  29. #       $datos="'','$Cadena','A','S'";
  30. #  &IncDaTb("PruebasUTF8","$datos");   
  31.         binmode STDOUT, ":utf8";
  32.         print "Content-type: text/html\n\n";
  33.        
  34.         print $Cadena;
  35. }
  36.  
  37.  
  38. sub Parametros1  
  39. {
  40.         if ($ENV{'REQUEST_METHOD'} eq "POST")
  41.         {
  42.                 read (STDIN, $Entrada1, $ENV{'CONTENT_LENGTH'});
  43.  
  44.                 $Entrada4 = $ENV{'QUERY_STRING'};
  45.                 $Entrada4=~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
  46.                 $Entrada4=~ s/\+/ /g;
  47.  
  48.                 ($Entrada2, $Entrada3) = split(/§-§/, $Entrada4);
  49.                 ($Nam3, $Val3) = split(/=/, $Entrada3);
  50.                 $FORM{$Nam3} = $Val3;
  51.  
  52.                 @ValMar = split(/&/, $Entrada2);          # Incorporado para recoger los valores posteriores enviados despues de la ? con metodo POST
  53.                 $Marca = shift(@ValMar);
  54.                 foreach $ValMar1 (@ValMar)
  55.                 {
  56.                         ($Nam2, $Val2) = split(/=/, $ValMar1);
  57.                         $FORM{$Nam2} = $Val2;
  58.                 }
  59.  
  60.                 #       ASV: 20121114
  61.  
  62.                 $Entrada1=~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
  63.                 $Entrada1=~ s/\+/ /g;
  64.  
  65.                 #       ASV: 20121114
  66.  
  67.                 @ValEnt = split(/&/, $Entrada1);
  68.         }
  69.         elsif ($ENV{'REQUEST_METHOD'} eq "GET")
  70.         {
  71.                 $Entrada1 = $ENV{'QUERY_STRING'};
  72.                
  73.                 #       ASV: 20121114
  74.  
  75.                
  76.                 $Entrada1=~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
  77.                 $Entrada1=~ s/\+/ /g;
  78.  
  79.                 #       ASV: 20121114
  80.  
  81.  
  82.                 ($Entrada1,$Entrada2) = split(/§-§/, $Entrada1); #Incorporado para controlar los valores posteriores a §-§
  83.                 @ValEnt = split(/&/, $Entrada1);
  84.                 $Marca = shift(@ValEnt);
  85.                 @ValMar = split(/&/, $Entrada2);
  86.         }
  87.         else
  88.         {
  89.                 $Entrada1 = $ARGV[0];
  90.         }
  91.  
  92.         foreach $ValEnt1 (@ValEnt)
  93.         {
  94.                 ($Nam1, $Val1) = split(/=/, $ValEnt1);
  95.                 $FORM{$Nam1} = $Val1;
  96.         }
  97. }
  98.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
lr_emilio
Perlero nuevo
Perlero nuevo
 
Mensajes: 25
Registrado: 2013-08-01 11:57 @539

Re: use UTF8, utf8 NO BOM y librerías

Notapor explorer » 2013-09-18 21:16 @928

Lo primero, es decidir el método de diálogo con el servidor: GET o POST (puede haber alguno más, pero estos son los tradicionales).

Cuando pones algo como esto:

<FORM id="F0" name="F0" method="POST" action="cgi-bin/pruebautf8.pl?A1">

estás indicando que vas a usar el método POST (los parámetros se transmitirán por la entrada estándar), pero en el URL estás poniendo "?A1", como si quisieras usar el método GET.

Hacer esto no es lo normal. Se puede, desde luego, pero el programa, en el servidor, debe acordarse de leer tanto la entrada estándar como los parámetros pasados en la URL.

Y el envío de parámetros es distinto (distinta codificación) según se envíen por GET o por POST. Supongamos que tenemos este formulario:
Sintáxis: [ Descargar ] [ Ocultar ]
Using html4strict Syntax Highlighting
  1.   <form id="F0" name="F0" method="GET" action="cgi/pruebautf8.pl">
  2.     <label for="cadena">Escribir caracteres para guardar en la base</label>
  3.     <input type="hidden" name="t" value="A1">
  4.     <input type="text" name="cadena" id="cadena" value="">
  5.     <br><br>
  6.     <input type="submit" value="Guardar sin Ajax">
  7.   </form>
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Observa que he quitado el "?A1" del URL. Con una petición GET, el navegador web lo quitará y pondrá en su lugar los campos del formulario. Por eso he creado un campo oculto, de un parámetro llamado "t", con el valor 'A1'.

Cuando el usuario pulsa el botón de envío, se compone la siguiente petición:

http://joaquin.com.es/cgi/pruebautf8.pl?t=A1&cadena=%C3%B1o%C3%B1%C3%A9

Vemos que los caracteres ("ñoñé") van codificados con dos bytes, pues van en la codificación indicada por la página, utf8.

Si cambiamos GET por POST, pues ya sabemos que los parámetros irán en un paquete HTTP especial, y entrarán por la entrada estándar a nuestro programa.

Entonces... en nuestro programa, debemos saber por dónde nos llegan los parámetros, y aplicar distintas estrategias para decodificarlos a partir de ese utf8.

Pongamos un escenario de ejemplo, basado en el tuyo. No es el mismo código HTML (había algunos errores HTML) ni código Perl (el tuyo es demasiado prolijo para lo que hoy en día se usa). Pero hacen básicamente lo mismo.
Sintáxis: (pruebautf8_get.html) [ Descargar ] [ Ocultar ]
Using html4strict Syntax Highlighting
  1. <!DOCTYPE HTML>
  2. <html lang="es">
  3. <head>
  4.   <meta charset="utf-8">
  5.   <title>Prueba UTF8</title>
  6.  
  7.   <script type="text/javascript" language="javascript" src="/javascript/jquery/jquery.js"></script>
  8.   <script type="text/javascript" language="javascript">
  9.     $(document).ready(function() {
  10.       $("#conajax").click(function(e) {
  11.         e.preventDefault();
  12.         $.ajax({
  13.           url: "cgi/pruebautf8_get.pl",
  14.           cache: false,
  15.           async: false,
  16.           type: "GET",
  17.           dataType: "html",
  18.           data: {
  19.                   t:'A2',
  20.                   cadena:$("#cadena").val()
  21.           },
  22.           success: function(data) {
  23.             alert(data);
  24.           }
  25.         });
  26.       });
  27.     });
  28.   </script>
  29. </head>
  30. <body>
  31.   <h1>Pruebas UTF8 para guardar en la base con ajax y sin ajax.</h1>
  32.   <h2>Caracteres especiales: [áéíóúüñçÁÉÍÓÚÜÑÇ]</h2>
  33.   <h3>Método GET</h3>
  34.   <form id="F0" name="F0" method="GET" action="cgi/pruebautf8_get.pl">
  35.     <label for="cadena">Escribir caracteres para guardar en la base</label>
  36.     <input type="hidden" name="t" value="A1">
  37.     <input type="text" name="cadena" id="cadena" value="">
  38.     <br><br>
  39.     <input type="submit" value="Guardar sin Ajax">
  40.   </form>
  41.   <br><br>
  42.   <a href="#" id="conajax">Guardar con Ajax</a>
  43. </body>  
  44. </html>
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Y este es el programa en la parte del servidor:
Sintáxis: (pruebautf8_get.pl) [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. # Joaquín Ferrero. 2013.
  3. # Prueba de Ajax con caracteres utf8
  4. #
  5. use Encode;                                  # de/codificación
  6. use CGI       qw(:standard      );           # procesamiento de los parámetros
  7. use CGI::Carp qw(fatalsToBrowser);           # en caso de error grave, verlos en la ventana del navegador web
  8.  
  9. use strict;                                  # activar modo estricto
  10. use warnings;                                # activar las advertencias
  11. use utf8;                                    # lo pongo porque más abajo hay algunos caracteres tildados
  12. use open OUT => ':utf8';                     # indico que las salidas de archivos serán en utf8,
  13. use open ':std';                             # especialmente STDIN, STDOUT y STDERR
  14.  
  15. my $parametro = param('t');                  # no decodificamos el parámetro "t" porque sabemos
  16.                                              # que solo va a valer dos valores: A1 y A2,
  17.                                              # y esos valores no tienen tildes :)
  18.  
  19. if    ( $parametro eq 'A1' ) { AlmacenaCaracteres('Sin') }   # caso sin Ajax
  20. elsif ( $parametro eq 'A2' ) { AlmacenaCaracteres('Con') }   # caso con Ajax
  21. else  {                                                      # algo pasó: pintar página de error
  22.     print
  23.         header(-charset => 'utf-8'),                         # vamos a sacar una cabecera HTTP. Indicamos además que lo mandaremos en utf8
  24.         start_html('Error'),                                 # empezamos a enviar el código HTML de la página de error. El título será 'Error'
  25.         h1('Error'),                                         # dentro de la página, un gigantesco 'Error' :)
  26.         hr,                                                  # una regla horizontal, para hacer bonito
  27.         p('Caracteres especiales: [áéíóúüñçÁÉÍÓÚÜÑÇ]'),      # una demostración de que la salida de caracteres tildados funciona bien
  28.         hr,
  29.         ;
  30.  
  31.     my @parametros = param();                                # volcado de todos los parámetros recibidos, en caso de haber alguno
  32.     foreach my $par ( @parametros ) {                        # recorremos todos los parámetros
  33.         print p($par);                                       # primero ponemos en un párrafo, el nombre del párrafo
  34.         print blockquote(param( $par ));                     # seguido por el valor del parámetro
  35.     }
  36.  
  37.     print
  38.         end_html,                                            # terminamos la página de error
  39.         ;
  40. }
  41.  
  42. # fin de programa
  43.  
  44. # subrutinas
  45. sub AlmacenaCaracteres {
  46.     my $sin_con = shift;                                     # con gas o sin gas
  47.     my $cadena = decode('utf8', param('cadena'));            # decodificación del parámetro 'cadena'
  48.  
  49.     print                                                    # esta es la respuesta que mandamos fuera
  50.         header(                                              # creamos una cabecera HTTP
  51.             -charset => 'utf-8',                             # lo que vamos a enviar va a estar en utf8
  52.             -type    => 'text/plain',                        # vamos a enviar puro texto
  53.         ),
  54.         "$sin_con Ajax: $cadena",                            # y este es el texto que enviamos
  55.         ;
  56. }
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Lo que hacemos, principalmente, es decodificar los parámetros que nos llegan, porque sabemos que están en utf8. Los pasamos por decode() para obtener caracteres Unicode. Y finalmente, mandamos el resultado hacia el usuario. Como la salida estándar sabemos que la hemos declarado que estará en utf8 (la línea del use open), entonces todos los print() hacen la conversión hacia el utf8. Y como en la cabecera de respuesta siempre ponemos un charset utf-8, pues entonces el navegador sabe cómo mostrarlos correctamente.

Ahora pasemos al método POST. El HTML es el mismo, solo que cambiamos las tres apariciones 'GET' por 'POST':
Sintáxis: (pruebautf8_get.html) [ Descargar ] [ Ocultar ]
Using html4strict Syntax Highlighting
  1. <!DOCTYPE HTML>
  2. <html lang="es">
  3. <head>
  4.   <meta charset="utf-8">
  5.   <title>Prueba UTF8</title>
  6.  
  7.   <script type="text/javascript" language="javascript" src="/javascript/jquery/jquery.js"></script>
  8.   <script type="text/javascript" language="javascript">
  9.     $(document).ready(function() {
  10.       $("#conajax").click(function(e) {
  11.         e.preventDefault();  
  12.         $.ajax({
  13.           url: "cgi/pruebautf8.pl",
  14.           cache: false,
  15.           async: false,
  16.           type: "POST",
  17.           dataType: "html",
  18.           data: {
  19.                   t:'A2',
  20.                   cadena:$("#cadena").val()
  21.           },
  22.           success: function(data) {
  23.             alert(data);
  24.           }
  25.         });
  26.       });
  27.     });
  28.   </script>
  29. </head>
  30. <body>    
  31.   <h1>Pruebas UTF8 para guardar en la base con Ajax y sin Ajax.</h1>
  32.   <h2>Caracteres especiales: [áéíóúüñçÁÉÍÓÚÜÑÇ]</h2>
  33.   <h3>Método POST</h3>
  34.   <form id="F0" name="F0" method="POST" action="cgi/pruebautf8_post.pl">
  35.     <label for="cadena">Escribir caracteres para guardar en la base</label>
  36.     <input type="hidden" name="t" value="A1">
  37.     <input type="text" name="cadena" id="cadena" value="">
  38.     <br><br>
  39.     <input type="submit" value="Guardar sin Ajax">
  40.   </form>
  41.   <br><br>
  42.   <a href="#" id="conajax">Guardar con Ajax</a>
  43. </body>
  44. </html>
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
El programa será casi el mismo, pero ahora haremos que la respuesta con Ajax sea solo texto (va a ser impreso solo en un alert()), mientras que la respuesta sin Ajax, será una página HTML completa:
Sintáxis: (pruebautf8_post.pl) [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. # Joaquín Ferrero. 2013.
  3. # Prueba de Ajax con caracteres utf8
  4. #
  5. use Encode;
  6. use CGI       qw(:standard      );
  7. use CGI::Carp qw(fatalsToBrowser);
  8.  
  9. use strict;
  10. use warnings;
  11. use utf8;
  12. use open OUT => ':utf8';
  13. use open ':std';
  14.  
  15.  
  16. my $parametro = param('t');
  17.  
  18. if    ( $parametro eq 'A1' ) { AlmacenaCaracteresSinAjax() }
  19. elsif ( $parametro eq 'A2' ) { AlmacenaCaracteresConAjax() }
  20. else  {
  21.     print
  22.         header(-charset => 'utf-8'),
  23.         start_html('Error'),
  24.         h1('Error'),
  25.         hr,
  26.         p('Caracteres especiales: [áéíóúüñçÁÉÍÓÚÜÑÇ]'),
  27.         hr,
  28.         ;
  29.  
  30.     my @parametros = param();
  31.     foreach my $par ( @parametros ) {
  32.         print p($par);
  33.         print blockquote(param( $par ));
  34.     }
  35.  
  36.     print
  37.         end_html,
  38.         ;
  39. }
  40.  
  41. #
  42. sub AlmacenaCaracteresSinAjax {
  43.     my $cadena = decode('utf8', param('cadena'));       # Decodificación
  44.  
  45.     print                                               # vamos a devolver una página HTML entera
  46.         header(
  47.             -charset => 'utf-8',
  48.             -type    => 'text/html',
  49.         ),
  50.         start_html,
  51.         "Sin Ajax: $cadena",
  52.         end_html,
  53.         ;
  54. }
  55.  
  56. sub AlmacenaCaracteresConAjax {
  57.     my $cadena = decode('utf8', param('cadena'));       # Decodificación
  58.  
  59.     print                                               # en este caso sacamos solo texto
  60.         header(
  61.             -charset => 'utf-8',
  62.             -type    => 'text/plain',
  63.         ),
  64.         "Con Ajax: $cadena",
  65.         ;
  66. }
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Hacemos exactamente el mismo procesamiento que hacíamos antes, con los parámetros. El módulo CGI se encarga de recogerlos desde la salida estándar, sabiendo si llegan por GET o POST, y los va dejando para que los obtengamos con param(). Solo nos queda hacer el decode(), y ya está. La pequeña diferencia con el programa anterior es que en el modo sin Ajax, se devuelve un código HTML completo. Esto es lo que genera, en ese caso:
Sintáxis: [ Descargar ] [ Ocultar ]
Using html4strict Syntax Highlighting
  1. <!DOCTYPE html
  2.         PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  3.          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  4. <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
  5. <head>
  6. <title>Untitled Document</title>
  7. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  8. </head>
  9. <body>
  10. Sin Ajax: ñoñé
  11. </body>
  12. </html>
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Y el resultado sale correcto, igual que antes con GET.

Aquí surge una duda: ¿por qué no activamos, con 'use open', el decodificado de la entrada estándar? Si vamos a recibir la información por la entrada estándar -método POST-, sería lógico pensar que podemos ahorrarnos las llamadas decode().

Bueno, pues resulta que no debemos hacerlo: los datos enviados sí que llegan por la entrada estándar, pero no llegan como una ristra de bytes codificados en utf8, sino codificados igual a como los recibimos vía GET (codificados en URL). Además, estaremos marcando toda la entrada como que ya está codificada en utf8 -cuando en realidad no ha hecho nada-, y Perl pondrá una bandera especial para indicarlo. Si intentamos sacar los datos de forma directa, saldrán mal (no fueron decodificados). Aún más estropicio: si los pasamos por decode() sí que los veremos bien, pero no si lo pasamos por decode_utf8(). Según el manual de Encode, estas dos líneas deberían hacer lo mismo:

my $cadena = decode('utf8', param('cadena'));
my $cadena = decode_utf8(param('cadena'));

pero no es así (al menos hasta la v2.44 de Encode; la v2.55 ya está corregida). Resulta que decode_utf8() comprueba antes de hacer la decodificación si los datos ya están en utf8, y si es así, no hace nada.

Como resulta que los datos sí están marcados como utf8 (por efecto del 'use open'), entonces no realiza ninguna decodificación.

Así que lo mejor, en este caso, es no decodificar la entrada estándar de forma automática, y usar el decode() del Encode, para esa tarea.


Finalmente, queda el caso más peculiar, que es el que propones: usar el método POST, pero con un URL en el que aparece un parámetro. Aquí tenemos que analizar tanto lo que nos llega por POST como la propia URL. Por fortuna, CGI nos ayuda bastante:
Sintáxis: (pruebautf8_post_url.html) [ Descargar ] [ Ocultar ]
Using html4strict Syntax Highlighting
  1. <!DOCTYPE HTML>
  2. <html lang="es">
  3. <head>
  4.   <meta charset="utf-8">
  5.   <title>Prueba UTF8</title>
  6.  
  7.   <script type="text/javascript" language="javascript" src="/javascript/jquery/jquery.js"></script>
  8.   <script type="text/javascript" language="javascript">
  9.     $(document).ready(function() {
  10.       $("#conajax").click(function(e) {
  11.         e.preventDefault();
  12.         $.ajax({
  13.           url: "cgi/pruebautf8.pl?t=A2",
  14.           cache: false,
  15.           async: false,
  16.           type: "POST",
  17.           dataType: "html",
  18.           data: {
  19.                   cadena:$("#cadena").val()
  20.           },
  21.           success: function(data) {
  22.             alert(data);
  23.           }
  24.         });
  25.       });
  26.     });
  27.   </script>
  28. </head>
  29. <body>
  30.   <h1>Pruebas UTF8 para guardar en la base con Ajax y sin Ajax.</h1>
  31.   <h2>Caracteres especiales: [áéíóúüñçÁÉÍÓÚÜÑÇ]</h2>
  32.   <h3>Método POST</h3>
  33.   <form id="F0" name="F0" method="POST" action="cgi/pruebautf8_post_url.pl?t=A1">
  34.     <label for="cadena">Escribir caracteres para guardar en la base</label>
  35.     <input type="text" name="cadena" id="cadena" value="">
  36.     <br><br>
  37.     <input type="submit" value="Guardar sin Ajax">
  38.   </form>
  39.   <br><br>
  40.   <a href="#" id="conajax">Guardar con Ajax</a>
  41. </body>  
  42. </html>
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
He movido los parámetros 't' de donde estaban y los he colocado en el URL, como lo tenías tú. Todo lo demás, es igual al método POST anterior.
El programa es exactamente igual, salvo que, por no aburrir, haremos una pequeña variación en la hoja HTML devuelta en Sin Ajax, para que muestre más información.
Sintáxis: (pruebautf8_post_url.pl) [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. # Joaquín Ferrero. 2013.
  3. # Prueba de Ajax con caracteres utf8
  4. #
  5. use Encode;
  6. use CGI       qw(:standard      );
  7. use CGI::Carp qw(fatalsToBrowser);
  8.  
  9. use strict;
  10. use utf8;
  11. use open qw(:utf8 :std);
  12.  
  13.  
  14. my $parametro = url_param('t');      # sabemos que el parámetro 't' viene por la URL
  15.  
  16. if    ( $parametro eq 'A1' ) { AlmacenaCaracteresSinAjax() }
  17. elsif ( $parametro eq 'A2' ) { AlmacenaCaracteresConAjax() }
  18. else {
  19.     print
  20.         header(-charset => 'utf-8'),
  21.         start_html('Error'),
  22.         h1('Error'),
  23.         hr,
  24.         p('Caracteres especiales: [áéíóúüñçÁÉÍÓÚÜÑÇ]'),
  25.         hr,
  26.         end_html,
  27.         ;
  28. }
  29.  
  30. #
  31. sub AlmacenaCaracteresSinAjax {
  32.     my $cadena = decode('utf8', param('cadena'));      # Decodificación
  33.  
  34.     print
  35.         header(
  36.             -charset => 'utf-8',
  37.             -type    => 'text/html',
  38.         ),
  39.         start_html,
  40.         p("$^V $Encode::VERSION"),                     # Versiones de Perl y Encode
  41.         p("Sin Ajax: $cadena. " . length($cadena) . " " . (utf8::is_utf8($cadena) ? 'si':'no') . ' es utf8'),
  42.         pre(unpack 'H*', $cadena),                     # vemos la ristra de octetos que tenemos en $cadena
  43.         end_html,
  44.         ;
  45. }
  46.  
  47. sub AlmacenaCaracteresConAjax {
  48.     my $cadena = decode('utf8', param('cadena'));       # Decodificación
  49.  
  50.     print
  51.         header(
  52.             -charset => 'utf-8',
  53.             -type    => 'text/plain',
  54.         ),
  55.         "Con Ajax: $cadena",
  56.         ;
  57. }
Coloreado en 0.006 segundos, usando GeSHi 1.0.8.4
Como sabemos que recibimos un parámetro por la URL, pues tenemos que usar url_param() para acceder a él.

Esto es lo que sale en el access.log del servidor web:

xx.xx.xxx.xxx - - [19/Sep/2013:02:55:07 +0200] "POST /cgi/pruebautf8_post_url.pl?t=A1 HTTP/1.1" 200 532 "http://joaquin.com.es/pruebautf8_post_url.html"

A mi no me gusta nada esta mezcla de parámetros, unos en la URL, y otros por POST... pero bueno, funciona.

Y ya está. El truco está en lo siguiente:
  • el 'use open' determina que lo que recibimos y enviamos será en codificación utf8, así que nos simplifica la conversión de lo que enviamos fuera
  • el decode() de Encode nos sirve para decodificar lo que nos llega por los parámetros, ya sea por GET (en el URL) o por POST (vía entrada estándar)
  • el charset a 'utf-8' (atento al guión '-'), que ponemos en todas las cabeceras HTTP de respuesta, para que el navegador sepa qué va a recibir

Por complementar: se puede simplificar el programa aún más.

Si el programa que estamos haciendo no va a recibir archivos binarios, sino solo parámetros escalares normales (texto), podemos decirle a CGI que realice la conversión a utf8 por nosotros. Supongamos que usamos la versión POST del HTML anterior. El programa queda así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. # Joaquín Ferrero. 2013.
  3. # Prueba de Ajax con caracteres utf8
  4. #
  5. use CGI       qw(:standard :utf8);          # aquí es donde pedimos a CGI la decodificación utf8 automática de los parámetros
  6. use CGI::Carp qw(fatalsToBrowser);
  7.  
  8. use strict;
  9. #use utf8;                                  # no es necesario, porque en este programa no hay caracteres tildados
  10. use open OUT => ':utf8';                    # Salida en utf8
  11. use open ':std';                            # incluida la salida estándar
  12.  
  13. my $parametro = param('t');
  14.  
  15. if    ( $parametro eq 'A1' ) { AlmacenaCaracteresSinAjax() }
  16. elsif ( $parametro eq 'A2' ) { AlmacenaCaracteresConAjax() }
  17.  
  18. #
  19. sub AlmacenaCaracteresSinAjax {  
  20.     my $cadena = param('cadena');           # no es necesaria ninguna decodificación
  21.  
  22.     print
  23.         header(
  24.             -charset => 'utf-8',
  25.             -type    => 'text/html',
  26.         ),
  27.         start_html,
  28.         p("Sin Ajax: $cadena. "),
  29.         end_html,
  30.         ;
  31. }
  32.  
  33. sub AlmacenaCaracteresConAjax {  
  34.     my $cadena = param('cadena');           # no es necesaria ninguna decodificación
  35.  
  36.     print
  37.         header(
  38.             -charset => 'utf-8',
  39.             -type    => 'text/plain',
  40.         ),
  41.         "Con Ajax: $cadena",
  42.         ;
  43. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Los dos 'use open' equivalen también a un simple binmode(STDOUT, ':utf8'), pero el primer 'use open' alcanza a todos los archivos abierto en escritura, que tengamos en el programa.

Perdón por el rollo, pero creo que así queda más claro.

P.D.: Fíjate en la cantidad de líneas que módulos como CGI nos ahorran: se encarga tanto de leer los parámetros como de generar el HTML. No vale ni es el indicado para todos los casos, pero es perfecto para ejemplos pequeños.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: use UTF8, utf8 NO BOM y librerías

Notapor lr_emilio » 2013-09-19 04:34 @232

Muchas gracias por las respuestas explorer.

El tema de mezclar variables en la URL para luego mandar el formulario por POST sé que no está bien pero el motivo de hacerlo así es que hay un centenar de webs ya hechas y funcionando de esta manera cgi-bin/archivo.pl?A1 donde A1 es la "marca" a la función que debería de ejecutarse en el .pl y luego los inputs aparte los recojo con $Form{'input_name'}. Supongo que cuando se hizo la función &parametros1 en mi librería no existía el módulo CGI...

Añadir el input hidden con la marca en todas las webs que tengo es algo inviable por la gran cantidad que tengo...
El lenguaje Perl que utilizo es bastante antiguo pero estoy bastante restringido por este motivo.

He modificado la función &parametros1 que utilizo en todas las webs de la siguiente manera y me funciona:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use CGI qw(-utf8);                     # librería encargada de recoger datos por get/post en utf8
  2. use Encode;
  3. use utf8;
  4. $| = 1;
  5.  
  6. sub Parametros1 {
  7.     $q = CGI->new;
  8.  
  9.     # Recogemos $Marca.
  10.     $Entrada4 = $ENV{'QUERY_STRING'};
  11.     $Entrada4 =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
  12.     $Entrada4 =~ s/\+/ /g;
  13.     ( $Entrada2, $Entrada3 ) = split( /§-§/, $Entrada4 );
  14.     @ValMar = split( /&/, $Entrada2 );
  15.     $Marca = shift(@ValMar);
  16.  
  17.     # Recogemos el resto de parámetros, irán en el formato $FORM{'campo'}
  18.     for my $key ( $q->param() ) {
  19.         for my $value ( $q->param($key) ) {
  20.             $FORM{$key} = $value;
  21.         }
  22.     }
  23. }
  24.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Un saludo
lr_emilio
Perlero nuevo
Perlero nuevo
 
Mensajes: 25
Registrado: 2013-08-01 11:57 @539

Re: use UTF8, utf8 NO BOM y librerías

Notapor explorer » 2013-09-19 06:19 @305

Si lo que sigue a '?' es algo tan sencillo como 'A1', se puede simplificar aún más, pero por el código que pones, con un separador '§-§', me temo que no es así.

¿Puedes poner un ejemplo real de ese primer parámetro?
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: use UTF8, utf8 NO BOM y librerías

Notapor lr_emilio » 2013-09-19 09:14 @426

Lo que sigue a '?' suele ser una cadena de caracteres sencillos, del tipo: A1, A2, Z0...

Es simplemente recoger esa cadena y compararla en el .pl y ejecutar la función correspondiente...

Un saludo
lr_emilio
Perlero nuevo
Perlero nuevo
 
Mensajes: 25
Registrado: 2013-08-01 11:57 @539

AnteriorSiguiente

Volver a Web

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 0 invitados