• Publicidad

Salida de datos

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Salida de datos

Notapor josefog83 » 2007-04-27 15:12 @675

Buenas tarde, disculpen una preguntica a quien me pueda ayudar.
A mi me gustaría saber cómo puedo generar un formato de salida de una variable, es decir a mi me gustaría que un numero por ejemplo 1000000 que sale de una suma, salga reflejado en pantalla con el siguiente formato 1.000.000,00.
Gracias por su interés
y espero respuesta.
josefog83
Perlero nuevo
Perlero nuevo
 
Mensajes: 12
Registrado: 2006-10-17 10:03 @460

Publicidad

Notapor explorer » 2007-04-27 18:43 @821

Esa pregunta ya se hizo por aquí, me parece... hace cosa de dos años...

No lo he encontrado, pero recuerdo que era hacer un bucle a lo largo de la longitud de la cadena de derecha a izquierda e ir insertando el carácter '.' cada 3 caracteres.

Lo de los decimales es luego un simple print.

Vamos, en 4 líneas lo haces...

¡Ánimo!
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor josefog83 » 2007-04-28 22:12 @966

Buenas noches, gracias por tu respuesta, lo único fue que pensé que existiría una función ya creada que se pudiese utilizar, por eso pregunté.
Gracias e igual buscaré el foro donde esté esa respuesta.
Gracias
josefog83
Perlero nuevo
Perlero nuevo
 
Mensajes: 12
Registrado: 2006-10-17 10:03 @460

Notapor explorer » 2007-04-29 08:27 @394

Pues si... y no...

Sí que existe una forma de hacer lo que quieres... mejor dicho, hay más de una, pero de niveles distintos a los de una solución informática basada en un algoritmo.

Verás, el tema de la separación de los miles es algo que desde hace unos años está resuelto porque, según el país donde vivas, esos separadores:
* No existirán (o no están permitidos)
* Existen, y son los que concuerdan con tu salida desea
* Existen, pero no son los que quieres.

En los sistemas operativos actuales, las diferencias entre idiomas se llama 'localizaciones'. Y el ordenador 'sabe' en qué localización estás usando el sistema de las variables de entorno.

Así, en Linux (un sistema compatible con POSIX), yo tengo mis variables de entorno que indican en qué localización estoy:
Código: Seleccionar todo
explorer@casa:~/Documents/Software/Perl> locale
LANG=es_ES@euro
LC_CTYPE="es_ES@euro"
LC_NUMERIC="es_ES@euro"
LC_TIME="es_ES@euro"
LC_COLLATE="es_ES@euro"
LC_MONETARY="es_ES@euro"
LC_MESSAGES="es_ES@euro"
LC_PAPER="es_ES@euro"
LC_NAME="es_ES@euro"
LC_ADDRESS="es_ES@euro"
LC_TELEPHONE="es_ES@euro"
LC_MEASUREMENT="es_ES@euro"
LC_IDENTIFICATION="es_ES@euro"
LC_ALL=
Mi localización indica que es española, variante España, y que la codificación de los caracteres será compatible con el carácter del Euro (iso-8859-15).

Bien, pero analizando esta localización, nos damos cuenta de que, en cuestión de separación de miles, no está definida la agrupación de dígitos:
Código: Seleccionar todo
explorer@casa:~/Documents/Software/Perl> LC_NUMERIC=es_ES printf "%'.2f\n" 1234567809,34
1234567809,34
cosa que no sería lo mismo si yo fuera danés:
Código: Seleccionar todo
explorer@casa:~/Documents/Software/Perl> LC_NUMERIC=da_DK printf "%'.2f\n" 1234567809,34
1.234.567.809,34
o norteamericano:
Código: Seleccionar todo
explorer@casa:~/Documents/Software/Perl> LC_NUMERIC=en_US printf "%'.2f\n" 1234567809,34
1,234,567,809.34
Además, fíjate que he usado LC_NUMERIC para cambiar mi localización, porque el comando printf no distingue de si queremos sacar un número o una cantidad monetaria. La localización del dinero está indicada en LC_MONETARY y deberíamos usar la función strfmon() del C para hacer las conversiones de dinero a cadena de caracteres.

Bueno, todo esto funciona en las librerías actuales de C para los sistemas operativos modernos (excepto Microsoft, que prefiere ser incompatible, como siempre).

Pero... en el caso de Perl tenemos un 'ligero' problema: no sabe usar el flag 'radical' de conversión de miles que posee la función printf() del sistema, por lo que un programa en Perl como este:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use POSIX qw(locale_h);
setlocale(LC_ALL, "es_ES.ISO8859-15");
printf "%'.2f\n", 1234567809.34;
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4
sacará en pantalla algo como esto:
Código: Seleccionar todo
%'.2f

Vamos, que no.

Pero bueno, no todo está perdido. Puede que print, printf o sprintf no trabajen bien con la única función que nos haría falta para resolver tu problema, pero Perl tiene otras posibilidades: podemos hacer el trabajo de ese flag nosotros mismos; al fin y al cabo... todo consiste en agrupar los dígitos de salida junto con los caracteres de separación de miles.

Todo esto está documentado en la parte que comenta la función localeconv(). Incluso viene un ejemplo de generación de los números con agrupación de miles. Pero... ese ejemplo no funciona muy bien...

El ejemplo lee la configuración actual de la separación de miles en números:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl -l
require 5.004;
use Data::Dumper;
use POSIX qw(locale_h);
use strict;

setlocale(LC_ALL, "en_US");

# Obtener parámetros de formateo numérico
my ($separador_de_miles, $agrupacion) =
     @{localeconv()}{'mon_thousands_sep', 'grouping'};

print Dumper localeconv();
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
salida:
Código: Seleccionar todo
$VAR1 = {
          'n_sep_by_space' => 0,
          'thousands_sep' => ',',
          'p_sep_by_space' => 0,
          'grouping' => '',
          'p_cs_precedes' => 1,
          'int_frac_digits' => 2,
          'mon_grouping' => '',
          'n_sign_posn' => 1,
          'currency_symbol' => '$',
          'int_curr_symbol' => 'USD ',
          'negative_sign' => '-',
          'p_sign_posn' => 1,
          'frac_digits' => 2,
          'n_cs_precedes' => 1,
          'decimal_point' => '.',
          'mon_thousands_sep' => ',',
          'mon_decimal_point' => '.'
        };
en el caso de ser norteamericano. Ves que la separación de miles tanto en números como en dinero es el carácter ',', por lo que, correctamente, la expresión regular que sigue es capaz de insertar ese separador entre la agrupación de caracteres (que es (3, 3) aunque no se vea en la salida de grouping y mon_grouping).

Bueno, el problema es que en Español, el carácter de separación de miles es el '.', y ya sabemos que ese carácter tiene un significado especial dentro de las expresiones regulares. Con setlocale(LC_ALL, "es_ES\@euro");, sale:
Código: Seleccionar todo
$VAR1 = {
          'n_sep_by_space' => 1,
          'currency_symbol' => '€',
          'p_sign_posn' => 1,
          'negative_sign' => '-',
          'int_curr_symbol' => 'EUR ',
          'frac_digits' => 2,
          'p_sep_by_space' => 1,
          'n_cs_precedes' => 1,
          'p_cs_precedes' => 1,
          'decimal_point' => ',',
          'mon_grouping' => '',
          'int_frac_digits' => 2,
          'n_sign_posn' => 1,
          'mon_thousands_sep' => '.',
          'mon_decimal_point' => ','
        };

Por ello, no podemos sin más meterlo dentro de ella. Hay que escaparlo. Con la ayuda de los indicadores de escapado de caracteres \Q y \E, será fácil hacerlo.

En resumen, el programa Perl que hace la presentación de cifras en formato monetario según la localización actual del sistema en que se ejecuta debería ser este (miles.pl):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
require 5.004;
use Data::Dumper;
use POSIX qw(locale_h);
use strict;

# Cambio de localizacion
#setlocale(LC_ALL, "es_ES\@euro");

# Obtener parámetros de formateo numérico
my ($separador_de_miles, $agrupacion) =
     @{localeconv()}{'mon_thousands_sep', 'mon_grouping'};

# Aplicar valores por defecto en caso de que falten
$separador_de_miles = '.' unless $separador_de_miles;

#print "separador: ->$separador_de_miles<-. Grupos: ->$agrupacion<-\n";

# grouping y mon_grouping son listas empaquetadas
# de pequeños números enteros (caracteres) indicando
# la agrupación (thousand_seps y mon_thousand_seps
# indican los grupos a formar) de números y cantidades
# monetarias. El significado de los enteros es:
# 255 significa que no hay más agrupación, 0 significa
# repetir la agrupación anterior, 1-254 significa usar
# este valor como actual agrupación. Agrupación va de
# derecha a izquierda (de bits bajos a altos).
# En el ejemplo de abajo sólo usamos el primer valor
# indicado (sea el que sea).
my @agrupacion;
if ( $agrupacion ) {
    # Desempaquetamos la lista de agrupaciones
    @agrupacion = unpack("C*", $agrupacion);
    #print "Agrupación: @agrupacion\n";
} else {
    # Por defecto, indicamos que la agrupación es
    # de 3 en 3 dígitos
    @agrupacion = (3);
}

# Formateo de los valores pasados por línea de comandos
for ( @ARGV ) {
    $_ = int;    # Sólo haremos una conversión de la parte entera
    1 while
        s/(\d)(\d{$agrupacion[0]}($|\Q$separador_de_miles\E))/$1$separador_de_miles$2/;
    print "$_\n";
}
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Con una entrada como: miles.pl 1234567890, la salida es 1.234.567.890.

El funcionamiento de la expresión regular es esta:
* por defecto, Perl intenta hacer coincidencias con la parte más a la derecha del patrón, por lo que se podría decir que estamos primero mirando de derecha a izquierda
* y a la derecha hay dos opciones: o fin de de línea ($) o un carácter anterior de $separador_de_miles. Aquí estaba el problema comentado antes, por lo que necesitamos 'escapar' los posibles caracteres especiales, con \Q y \E
* delante de estos esperamos una agrupación de dígitos (\d{$agrupacion[0]})
* precedidos a su vez por al menos un dígito más (\d).
* la conversión es introducir un nuevo separador de miles delante de la agrupación encontrada.

Hay una línea comentada, la del setlocale, que, de usarse, nos permitirá mostrar distintas presentaciones según la localización que elijamos, independientemente de la del sistema. Si usamos setlocale(LC_ALL, "en_US");, para el ejemplo anterior saldrá 1,234,567,890.

Como ves, no hay una función (al menos ahora) que resuelva el problema, pero con un par de líneas lo tienes. Básicamente, el while hace toda la conversión por lo que es casi lo único que necesitas.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Gracias

Notapor josefog83 » 2007-05-02 09:17 @429

Gracias por la respuesta tan completa.
Trabajare en entenderla y aplicarla gracias
josefog83
Perlero nuevo
Perlero nuevo
 
Mensajes: 12
Registrado: 2006-10-17 10:03 @460

Formato de miles

Notapor saga.mazyncoh » 2007-05-07 15:30 @688

Que tal, tal vez esta solución te pueda ayudar:

Para separador de miles usando comas ( , ):
1 while ( $total=~ s/^(-?\d+)(\d{3})/$1,$2/ );

Para separador de miles usando puntos ( . ) :
1 while ( $total=~ s/^(-?\d+)(\d{3})/$1.$2/ );



Saludos
saga.mazyncoh
Perlero nuevo
Perlero nuevo
 
Mensajes: 1
Registrado: 2007-05-07 15:24 @683


Volver a Básico

¿Quién está conectado?

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

cron