• Publicidad

Editar ficheros .dat en un solo .dat y por columnas

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

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor enric73 » 2012-03-27 17:18 @763

¡Buenas noches, explorer!

Lo que necesito es leer todos los archivos, ordenarlos por hora, e intercalar los archivos 'U' y 'V', ¡pero con una columna para cada archivo! Como en el script anterior, pero intercalando las componentes 'U' y 'V'. La hora aparece al final de cada archivo_hh.dat.

Aquí muestro un script en el que se hace lo mismo (bueno,las unidades en las columnas no están alineadas si hay algún valor negativo) pero prefiero conocer cómo se haría con su script anterior que llama use Data::CTable.

Muchas gracias!

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use v5.10;
  3.  
  4. # use Class::Date qw(date now);
  5.  
  6. # The file list
  7. my @arxiusu = </home/enric/ASCII/2012032300/UGRD10m*.dat>;
  8. my @arxiusv = </home/enric/ASCII/2012032300/VGRD10m*.dat>;
  9.  
  10. # Loop over the first filelist and count with $i
  11. # You can open file u and v at position $i in the file arrays (in @arxiusu and @arxiusv)
  12. $i = 0;
  13. foreach $arxiuu (@arxiusu) {
  14.     print "reading ", $arxiuu, "\n";
  15.     @uval = ();
  16.     open( ARXU, "<$arxiusu[$i]" );
  17.     while (<ARXU>) {
  18.         chomp;
  19.         next unless length;
  20.         $val = sprintf( "%.2f", $_ );
  21.  
  22.         # write the line (which is one value) into array @uval)
  23.         push( @uval, $val );
  24.     }
  25.     close(ARXU);
  26.     @vval = ();
  27.     open( ARXV, "<$arxiusv[$i]" );
  28.     while (<ARXV>) {
  29.         chomp;
  30.         next unless length;
  31.         $val = sprintf( "%.2f", $_ );
  32.  
  33.         # write the line (which is one value) into array @vval)
  34.         push( @vval, $val );
  35.     }
  36.     close(ARXV);
  37.     $j = 0;
  38.  
  39.     # now loop over array @uval and again count with $j, so you can read
  40.     # u and v at position $j in @uval and @vval
  41.     foreach $uval (@uval) {
  42.  
  43.         # make a pair of u\tv and store it in an array %data under key <$i>_<$j>
  44.         # $i is the counter for the files which is timestamp
  45.         # $j is the counter for the line in file, which is gridpoint
  46.         $data{ $i . "_" . $j } = $uval[$j] . "\t" . $vval[$j];
  47.         $j++;
  48.     }
  49.     $i++;
  50. }
  51.  
  52. # now foreach gridpoint loop over time, which is loop from $j=1 (ommit first line..., so start with 1)
  53. # to last file and inside this loop you loop over time.
  54. for ( $j1 = 1; $j1 < $j; $j1++ ) {
  55.     $line = "";
  56.     for ( $i1 = 0; $i1 < $i; $i1++ ) {
  57.         $line = $line . $data{ $i1 . "_" . $j1 } . "\t";
  58.     }
  59.     $line =~ s/\t$//g;
  60.     $output = $output . $line . "\n";
  61. }
  62. $file = "vents2.dat";
  63. open( DATA, ">$file" );
  64. print DATA $output;
  65. close(DATA);
Coloreado en 0.007 segundos, usando GeSHi 1.0.8.4
Última edición por explorer el 2012-03-27 17:22 @765, editado 1 vez en total
Razón: Formateado de código con Perltidy y poner marcas Perl
enric73
Perlero nuevo
Perlero nuevo
 
Mensajes: 154
Registrado: 2012-03-16 06:27 @311

Publicidad

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor explorer » 2012-03-27 18:51 @827

Si suponemos que hay tantos ficheros 'U' como 'V', lo que hay que hacer es 'barajarlos'.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my @archivos_U = sort </home/enric/ASCII/2012032300/UGRD10m*.dat>;
  2. my @archivos_V = sort </home/enric/ASCII/2012032300/VGRD10m*.dat>;
  3.  
  4. use List::MoreUtils 'zip';
  5.  
  6. my @archivos = zip @archivos_U, @archivos_V;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

La operación sort() es fundamental, para que los ficheros queden ordenados por hora.
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

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor enric73 » 2012-03-30 12:09 @548

Hola, explorer. Buenas tardes, gracias por la respuesta.

He utilizado los comandas que me has facilitado, pero no me funciona.

Le adjunto el script que tengo.

Me comenta que la operación sort() ordenada los ficheros por hora. En mi caso, la hora de cada fichero está en el mismo nombre del fichero, es decir, UGRD10m00.dat, VGRD10m00.dat, UGRD10m03.dat, VGRD10m03.dat, la hora es 00, 03, 06, 09, cada 3 horas... Igualmente la operación sort() funciona de esta manera, creo que no, pienso que mira la hora de creación del fichero, ¿no? Si es así, ¿cómo podría hacer que mire la hora a partir del nombre del fichero...?

Cuando ejecuto el script, me genera la carpeta, el fichero.dat, con la fecha pero sin datos. No sale ningún error de compilación... Gracias

#!/usr/bin/perl
use v5.10;
use Data::CTable;
use POSIX 'strftime';
use Class::Date qw(date now);
use List::MoreUtils 'zip';


my @arxius_U = sort </home/enric/ASCII/2012033000/UGRD10m*.dat>;
my @arxius_V = sort </home/enric/ASCII/2012033000/VGRD10m*.dat>;

my @arxius = zip @arxius_U, @arxius_V;
my $tabla = Data::CTable->new();

# {
for my $i ( 1 .. @arxius ) {
my $col = Data::CTable->new;

$col->read(
_FileName => $arxius[$i-1],
_HeaderRow => 0,
_FieldList => [ $i ],
_CacheOnRead => 0,
);

$col->row_delete(0);

$tabla->combine( $col );

}

for my $i ( 1 .. @arxius) {
my @indexs = @{$tabla->all()};
my $files = $tabla->col($i);

my $ample_maxim = 0;

for my $j (@indexs) {

my $valor = $files->[$j] // '';

my $ample = length $valor;

if ($ample_maxim < $ample) {
$ample_maxim = $ample;
}
}

for my $j (@indexs) {
$files->[$j]
= sprintf "%*.2f", $ample_maxim, $files->[$j] // 0;

}

}

# Posem la data
my $llarg = $tabla->length();
$tabla->row_set($llarg, {

1 => strftime('%Y%m%d' , localtime),
});
$tabla->row_move($llarg, 0);


# Sortida
$tabla->write(
_FileName => '/home/enric/ASCII/2012033000/vents.dat',
_FDelimiter => "\t",
_HeaderRow => 0,
);
enric73
Perlero nuevo
Perlero nuevo
 
Mensajes: 154
Registrado: 2012-03-16 06:27 @311

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor explorer » 2012-03-30 16:38 @735

enric73 escribiste:Me comenta que la operación sort() ordenada los ficheros por hora. En mi caso, la hora de cada fichero está en el mismo nombre del fichero, es decir, UGRD10m00.dat, VGRD10m00.dat, UGRD10m03.dat, VGRD10m03.dat, la hora es 00, 03, 06, 09, cada 3 horas... Igualmente la operación sort() funciona de esta manera, creo que no, pienso que mira la hora de creación del fichero, ¿no? Si es así, ¿cómo podría hacer que mire la hora a partir del nombre del fichero...?
Si no te lo crees, ejecuta esa operación aparte:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my @arxius_U = sort </home/enric/ASCII/2012033000/UGRD10m*.dat>;
  2. print "@arxius_U\n";
  3. __END__
  4.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Y mira a ver si los archivos salen ordenados por fecha o no. sort() ordena lo que tiene a la derecha. Y en este caso son los nombres de los archivos.

enric73 escribiste:Cuando ejecuto el script, me genera la carpeta, el fichero.dat, con la fecha pero sin datos. No sale ningún error de compilación...
Pues... no sé lo que puede pasar... Si no sale nada, es que los archivos estarían vacíos, o que no hay ningún archivo en el listado de @arxius.

Puedes probar a ejecutar el programa por partes (pon un exit() para que el programa se pare). Y en cada parte, pones un print() para sacar los datos, para ver si ha leído correctamente los archivos. Por ejemplo, sacar el contenido de @arxius para comprobar que, efectivamente, ha leído y ordenado los archivos. O haz un $tabla->write() después del primer bucle, para ver si lo has combinado correctamente.
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

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor enric73 » 2012-04-03 05:34 @273

explorer,

muchas gracias. Realmente el comando sort() va muy bien, me ordena los ficheros por orden del 0 al 99, pero si tengo algún fichero por encima del 99, por ejemplo el 101, 102, me lo cuela a partir del 10, los ordena entre el 10 y el 20.

¿Cómo puedo evitar que las centenas queden entre el 10 y el 20?

A parte de esto he añadido un argumento para que me guarde el vent.dat automáticamente en una carpeta con la fecha del día y que vaya a buscar los ficheros automáticamente en carpetas descritas por fechas, y funciona.

if (@ARGV != 1 )
{
us_programa();
}

# Data o hora
chomp($flag = $ARGV[0]);
sub us_programa
{
print "Us del programa: Ventsdat yyyymmdd[hh]\n";
print "Ex: Ventsdat 2009102800\n";

exit(1);
}

Me funciona.

También necesito añadir un segundo argumento en la primera fila, al lado de la fecha el siguiente dato como argumento 000000 o 120000

es decir

20120403 000000

¿Cómo podría hacerlo para que me aparezca al lado de la fecha en la primera línea?
# Posem la data
my $llarg = $tabla->length();
$tabla->row_set($llarg, {

1 => strftime('%Y%m%d' , localtime),
});
$tabla->row_move($llarg, 0);

Los argumentos (de momento 1) los entro de la siguiente manera desde el crontab de shell,
00 06 * * */..../Vent.dat `date +\%Y\%m\%d
--date='today'`00


Lógicamente si introduzco un nuevo argumento tendré que poner

if (@ARGV != 2 )
{
us_programa();
}

# Fecha
chomp($flag = $ARGV[0]);

# Hora simulación
chomp($hs = $ARGV[1]);

sub us_programa
{
print "Us del programa: Ventsdat yyyymmdd[hh] 000000\n";
print "Ex: Ventsdat 2009102800 000000\n";

exit(1);
}

¡Muchas gracias, master!
enric73
Perlero nuevo
Perlero nuevo
 
Mensajes: 154
Registrado: 2012-03-16 06:27 @311

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor explorer » 2012-04-03 09:52 @453

A ver... dijiste que el número que había en el nombre del archivo representaba las horas del archivo. Así que supuse que los números irían hasta las 23. ¿Cómo es posible que lleguen al 100?

Dicho de otra forma... si tenemos el nombre del archivo UGRD10m00.dat, ¿de qué partes consta y qué rangos de valores puede tomar?

Por ejemplo: "La primera letra es 'U' o 'V'". "Le sigue una cadena fija: "GRD". Y así...
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

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor enric73 » 2012-04-03 10:23 @474

Hola explorer,

Gracias por la ayuda.
  • Los ficheros .dat empiezan por U (componente U del viento) y por la V (componente Y del viento)
  • Luego viene la parte fija para las dos componentes GRD10m
  • Luego la hora de simulación del viento (para las dos componentes) pero que va sumándose cada 3 horas sin volver a las 01 horas después de las 24h. Mi rango va de 0 horas a 150 horas. Es decir, cuando llega a las 21 horas, luego vienen 24 horas, 27 horas... 102 horas...
enric73
Perlero nuevo
Perlero nuevo
 
Mensajes: 154
Registrado: 2012-03-16 06:27 @311

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor explorer » 2012-04-03 16:10 @715

Este mini programa sirve para que veas cómo se puede ordenar según esa cifra (que hay que extraer).
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use diagnostics;
  5.  
  6. my @archivos_U = <dir/UGRD10m*.dat>;
  7. my @archivos_V = <dir/VGRD10m*.dat>;
  8.  
  9. @archivos_U = ordena_por_hora(@archivos_U);
  10. @archivos_V = ordena_por_hora(@archivos_V);
  11.  
  12. sub ordena_por_hora {                                   # Transformada de Schwartz (leer al revés)
  13.     my $x;
  14.  
  15.     map  { $_->[1] }                                    # Nos quedamos con el segundo elemento (el archivo)
  16.     sort { $a->[0] <=> $b->[0] }                        # Ordenamos numéricamente y de forma ascendente
  17.     map  {
  18.         ($x) = $_ =~ /10m(\d+)/;                        # Extraemos la hora
  19.         [ $x, $_ ];                                     # Construimos array con hora y nombre archivo
  20.     }
  21.     @_                                                  # Para todos los archivos
  22. }
  23.  
  24. print "[", join("][", @archivos_U), "]\n";
  25. print "[", join("][", @archivos_V), "]\n";
  26.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Usamos una expresión regular para extraer la cifra. Si supiéramos que esa cifra tuviera siempre, por ejemplo, tres dígitos, entonces podríamos sustituir la expresión regular por una más rápida substr(). Mejor dicho: no haría falta hacer nada de esto porque con el mismo sort() estaría resuelto. Las quejas, al que se le ocurrió dar nombre a esos archivos de esa manera :) .

Con la fecha en la mano (quiero decir, en $x), construimos un mini array de dos elementos: la hora y el nombre original del archivo.

Solo queda hacer una transformada de Schwartz para que todo quede ordenado.

Esta es otra versión, más corta, pero más críptica :)
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. sub ordena_por_hora { ($a =~ /m(\d+)/)[0] <=> ($b =~ //)[0] };
  3. my @archivos_U = sort ordena_por_hora <dir/UGRD10m*.dat>;
  4. my @archivos_V = sort ordena_por_hora <dir/VGRD10m*.dat>;
  5. $" = '][';
  6. print "[@archivos_U]\n";
  7. print "[@archivos_V]\n";
  8.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Solo queda hace el zip para que haga el barajado de los nombres (alternancia), y ya está.
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

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor enric73 » 2012-04-04 09:54 @454

¡Hola, explorer!

¡Me ha funcionado de la primera manera! ¡Esto del Perl tiene realmente muchas posibilidades!

He probado la segunda manera, pero me sale error en la línea

sub ordena_por_hora { ($a =~ /m(\d+)/)[0] <=> ($b =~ //)[0] };

Illegal declaration of subroutine main::endre at ./Ventsdat.pl line 70

¿Quiere decir que habría de instalar el módulo main::endre con el CPAN?

¿Exactamente esta línea qué quiere decir?

sub ordena_por_hora { ($a =~ /m(\d+)/)[0] <=> ($b =~ //)[0] };

¡¡¡¡Realmente es críptica!!!! :(

Más preguntas. En la línea
sort { $a->[0] <=> $b->[0] } # Ordenamos numéricamente y de forma ascendente

¿$a se trata de los ficheros_U y $b de los ficheros_V? ¿el 0 de dentro qué significa?

map {
($x) = $_ =~ /10m(\d+)/; # Extraemos la hora


En este caso, el d+ es el número del fichero que corresponde a la hora, ¿no?

¡Última consulta!

También necesito añadir un segundo argumento en la primera fila, al lado de la fecha el siguiente dato como argumento: 000000 o 120000. Ya sé maniobrar con los argumentos, simplemente necesito añadir en la primera fila junto con la fecha el dato 000000 o 120000 como argumento cuando lanzo el script: ./Ventsdat 2012040300 000000 .

Es decir, necesito que en la primera fila salga

20120403 000000

¿Cómo podría hacerlo para que me aparezca al lado de la fecha en la primera línea?

# Pongo la data
my $llarg = $tabla->length();
$tabla->row_set($llarg, {

1 => strftime('%Y%m%d' , localtime),
});
$tabla->row_move($llarg, 0);

Los argumentos (de momento 1) los entro de la siguiente manera desde el crontab de shell,
00 06 * * */..../Vent.dat `date +\%Y\%m\%d --date='today'`00

Lógicamente si introduzco un nuevo argumento tendré que poner

if (@ARGV != 2 )
{
us_programa();
}

# Fecha
chomp($flag = $ARGV[0]);

# Hora simulación
chomp($hs = $ARGV[1]);

sub us_programa
{
print "Us del programa: Ventsdat yyyymmdd[hh] 000000\n";
print "Ex: Ventsdat 2009102800 000000\n";

exit(1);
}

¡Muchas gracias, explorer!
enric73
Perlero nuevo
Perlero nuevo
 
Mensajes: 154
Registrado: 2012-03-16 06:27 @311

Re: Editar ficheros .dat en un solo .dat y por columnas

Notapor explorer » 2012-04-04 11:37 @526

enric73 escribiste:¡Me ha funcionado de la primera manera! ¡Esto del Perl tiene realmente muchas posibilidades!
Por favor:
  • Recuerda que "Perl" (primera letra en mayúscula) es el nombre del lenguaje, y "perl" (en minúsculas) es el nombre del intérprete de Perl (el ejecutable)
  • Acuérdate de poner marcas de sintaxis Perl a tus trozos de código, para que queden mejor que no solo texto. Para ello, en el momento de escribir los mensajes, fíjate que hay un botón por encima de la caja de texto, que se llama Perl
  • Mi nombre es 'explorer', no 'Explorer' ;) (el segundo se refiere al programa de Micro$oft)

enric73 escribiste:He probado la segunda manera, pero me sale error en la línea

sub ordena_por_hora { ($a =~ /m(\d+)/)[0] <=> ($b =~ //)[0] };

Illegal declaration of subroutine main::endre at ./Ventsdat.pl line 70

¿Quiere decir que habría de instalar el módulo main::endre con el CPAN?
A mi no me da error. Apuesto a que tienes alguna línea con el 'endre' por algún sitio, alrededor de esa línea (no exactamente esa línea).

enric73 escribiste:¿Exactamente esta línea qué quiere decir?

sub ordena_por_hora { ($a =~ /m(\d+)/)[0] <=> ($b =~ //)[0] };

¡¡¡¡Realmente es críptica!!!! :(
Si no la entiendes es mejor que uses la primera solución. Ahí estamos definiendo una subrutina para sort(). En ella, las variables $a y $b representan dos elementos a ordenar. De cada elemento, le extraemos el número que sigue a la 'm'. La comparación será numérica (<=>). Y como $a aparece antes que la $b, será de forma ascendente. La expresión regular de $b es especial (//) Quiere decir que ahí Perl debe usar el mismo patrón que la última expresión regular usada (que en este caso, será la de la $a). Los números (\d+) que siguen a la 'm' queremos capturalos, así que están encerrados entre paréntesis. Encerramos además las expresiones regulares con otros paréntesis para ejecutarlos en contexto lista (nos devolverá la lista de elementos capturados por los paréntesis de captura). Y los subíndices '[0]' harán que nos quedemos al final con el primer elemento de la lista de elementos devuelta por los paréntesis de lista. En resumen, que de las expresiones regulares salen los números que estaban al lado de las 'm', y los comparamos con el operador matemático '<=>'. De resultas de esta comparación, sort() sabrá ordenar los valores $a y $b dentro de toda la lista de valores a ordenar.

enric73 escribiste:Más preguntas. En la línea
sort { $a->[0] <=> $b->[0] } # Ordenamos numéricamente y de forma ascendente

¿$a se trata de los ficheros_U y $b de los ficheros_V? ¿el 0 de dentro qué significa?
No. $a y $b se refieren a la misma familia de archivos. El 0 se refiere al primer elemento del array creado en la línea siguiente. Te puse el enlace a la Transformada de Schwartz...

enric73 escribiste:map {
($x) = $_ =~ /10m(\d+)/; # Extraemos la hora


En este caso, el d+ es el número del fichero que corresponde a la hora, ¿no?
No, es la hora: un conjunto de uno o más (+) dígitos (\d), que siguen a '10m'.

enric73 escribiste:simplemente necesito añadir en la primera fila junto con la fecha el dato 000000 o 120000 como argumento cuando lanzo el script: ./Ventsdat 2012040300 000000 .
Te vale con concatenarlo a la fecha. Usa el operador '.'. Más información en tu propio ordenador en perldoc perlop, y en la Web (traducido).
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

AnteriorSiguiente

Volver a Básico

¿Quién está conectado?

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