• Publicidad

Sumar y restar tiempos en un fichero de texto

Reglamento de Perl en Español

Sumar y restar tiempos en un fichero de texto

Notapor explorer » 2006-04-17 14:19 @638

INTRODUCCIÓN
Supongamos que tenemos un fichero de texto con la siguiente estructura:
Código: Seleccionar todo
1
00:00:37,243 --> 00:00:40,244
¿Hola? Probando, 1-2-3. ¿Hola?

2
00:00:41,645 --> 00:00:44,944
¡Siii! Bien, allá vamos.

3
00:00:45,306 --> 00:00:47,618
Diario de abordo... erm... uno.

4
00:00:48,319 --> 00:00:51,921
He decidido llevar un diario
de mi vida a bordo, y después
lanzarlo en una vaina.

Es un fichero que almacena los subtítulos de películas o grabaciones hechas en otros formatos. Si tenemos una película y el reproductor encuentra un fichero de este tipo con el mismo nombre, intentará colocar los textos en los tiempos indicados, como subtítulos de la película.

Pero suele ocurrir un problema...

En algunas ocasiones, no coinciden los tiempos que marcan los subtítulos con la línea de tiempos de la película. Quiero decir que a veces los subtítulos salen con algunos segundos de adelanto y en otras ocasiones, de retraso.

El objetivo es hacer un programa en Perl que nos permita reescribir el fichero de subtítulos, cambiando los tiempos por otros, al que le sumaremos o restaremos una cantidad de segundos.

ENTRADA
Al programa en Perl se le pasará como argumento el nombre del fichero con los subtítulos y la cantidad (positiva o negativa) de segundos que hay que modificar los tiempos.
Ejemplos:
Código: Seleccionar todo
subs_desvia.pl EnanoRojo_7x01.srt -12
subs_desvia.pl EnanoRojo_3x05.srt 92

SALIDA
La salida será hacia la salida estándar, un fichero con el mismo formato que el de entrada, pero con los tiempos cambiados.

FORMATO DEL FICHERO DE ENTRADA
El fichero es un conjunto de párrafos (conjunto de líneas separadas por una línea en blanco). Los finales de línea son del tipo msdos (\r\n).
Cada párrafo comienza por un número.
Le sigue una línea en que aparece los dos items de tiempo en que el subtítulo debe aparecer en pantalla. El formato de estos items es HH:MM:SS,ddd, siendo
HH : Hora, rellenado con ceros a la izquierda.
MM : Minutos, rellenado con ceros a la izquierda.
SS : Segundos, rellenado con ceros a la izquierda.
ddd: milesimas de segundo. Es necesario conservar esta cantidad en el fichero de salida.
Le sigue una o más líneas con el subtítulo.

FORMATO DEL FICHERO DE SALIDA
Igual que el de entrada.

PARTICIPACIÓN
Esta competición se realiza con el ánimo de que todo el mundo participe, en especial los principiantes, desarrollando un programa en nuestro lenguaje favorito.
Habrá menciones para el programa más claro, el más elegante, el más corto, el que realice su labor con más y menos módulos, la solución obvia, la más rápida y para los que empiezan a programar.
Como única excepción, no se podrá utilizar el módulo Subtitles (obviamente).
Los programas se deberán publicar a continuación de este. ¡Adelante!
Última edición por explorer el 2008-09-06 11:21 @515, editado 1 vez en total
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14475
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Publicidad

Notapor Perl user » 2006-04-17 22:22 @973

Esta es mi solución:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl

use strict;
use warnings;
use Time::Simple;

use constant ONE_HOUR => 60 * 60;

my ( $time_re, $add_time ) = ( qr/(?:\d{2}:?){3}/, $ARGV[1] );

sub parse_file {
    my ( $file ) = @_;
    return unless $file and defined $add_time;
    local $/ = "\r\n"x2;
    open my $fh, $file or die $!;
    print parse_record( $_ ) while <$fh>;
    close $fh;
}

sub parse_record {
    my ( $line ) = @_;
    my @times = $line =~ /$time_re/g;
    my @new_times =
        map Time::Simple->new( $_ ) + $add_time - ONE_HOUR, @times;
    $line =~ s/$times[$_]/$new_times[$_]/ for 0..@times - 1;
    return $line;
}

parse_file( $ARGV[0] );
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


Hay unas comentarios respecto al problema y a la solución:
1) El problema dice que la salida debe imprimirse a STDOUT, pero luego hace hincapié que debe almacenarse en un archivo del mismo formato (aunque igual tomándolo de STDOUT lo puedes redirigir a un archivo).
2) No dice que no puedas utilizar otros módulos diferentes a Subtitles, iba a utilizar Tie::File, pero igual salió pequeño aun sin el módulo, pero es importante mencionar que otros módulos están permitidos en caso de que se requiera.
3) Evité muchas validaciones, simplemente si no se provee los parámetros necesarios el programa termina.
4) La solución se planteó tomando EXACTAMENTE como indica la especificación del problema y con el archivo de ejemplo propuesto, para no salir con sorpresas de que debía hacer X o Y en caso de A o B sin estar en la especificación.
5) Aunque dice que el problema va dirigido principalmente para principiantes, solo propuse una solución no deseo competir :), el ocio tiende a obligarme realizar actividades como esta.

Saludos,
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor gaston » 2008-02-05 22:18 @971

Hola, qué tal. Hace unos dos meses aprox. que comencé con Perl.

Me parece excelente que hagan éstos concursos, al menos a mi me animó mucho a investigar para poder dar con la solución.

Por ser principiante, quizás el código no es el mejor, incluso me parece que es muy largo, pero creo que con un poco más de experiencia llegaré a acortarlo bastante (espero).

Acá les dejo mi solución. El primero es un módulo que es el encargado de hacer los cálculos y escribir el archivo.

El segundo archivo (subs_desvia.pl) es el cliente.

Comentario: le agregué un método para hacer el backup del archivo original. Eso solo.

Saludos.

Gastón

Subtitle.pm
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
package Subtitle;

        use strict;

        sub new {
                my $class = shift;
                my $obj = {};
               
                $obj->{-file}                   = undef;
                $obj->{-desvio}                 = 0;
                $obj->{-backup}                 = 0;
               
                # Chequeamos parámetros
                my %params = @_;
                for my $key (keys %params) {
                        $obj->{$key} = $params{$key} if exists($obj->{$key});
                       
                }
               
                bless $obj, $class;
               
                return $obj;
        }
       
        sub loadFile {
                my $obj = shift;
                $obj->{-file} = $_[0];
               
                # Validamos tipos de argumentos.
                die "El fichero $obj->{-file} no se pudo abrir.\n" unless open FILE, $obj->{-file};
        }
       
        sub loadDesvio {
                my $obj = shift;
               
                die "El parámetro de tiempo es incorrecto.\n" unless $obj->{-desvio} =~ /^([0-9]+|[-0-9]+)(s|m|h)*$/;
                my $sec = $_[0];
                # Si llega en minutos u horas, lo pasamos a segundos.
                if ( $_[0] =~ /^([0-9]+|[-0-9]+)m$/ ) {
                        $sec = $_[0] * 60;
                } elsif( $_[0] =~ /^([0-9]+|[-0-9]+)h$/ ) {
                        $sec = $_[0] * 3600;
                }
                $obj->{-desvio} = $sec;
        }
       
        # Genera un backup del original.
        sub makeBackup {
                my $obj = shift;
                $obj->{-backup} = 1;
        }
       
        # Escribe el archivo
        sub generate {
                my $obj = shift;
               
                open FILE, $obj->{-file};
                my @file = <FILE>;
                my $newSubtitles = "";
                for (@file) {
                        if ( /^[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{0,3}[ --> ]*[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{0,3}/ ) {
                                $newSubtitles .= $obj->_calculate();
                        } else {
                                $newSubtitles .= $_;
                        }
                }
                close FILE;
               
                # Generamos backup
                if ($obj->{-backup}) {
                        my $cont = 0;
                        while (1) {
                                if ( !-e $obj->{-file}.".bk$cont" ) {
                                        open FILE_SAVE, "> $obj->{-file}.bk$cont";
                                        for (@file) {
                                                print FILE_SAVE;
                                        }
                                        close FILE_SAVE;
                                        last;
                                }
                                $cont++;
                        }
                }
               
                # Guardamos el fichero (sobreescribimos)
                open FILE_SAVE, "> $obj->{-file}";
                print FILE_SAVE $newSubtitles;
                close FILE_SAVE;
        }
       
        # Calcula los tiempos y devuelve el string de tiempo con el formato.
        sub _calculate {
                my $obj = shift;
               
                my $newTime = "";
                my @line = split / --> /, $_;
                my $addArrow = 1;
               
                # Discriminamos el string de tiempo y devolvemos el nuevo.
                for (@line) {
                        my @time = split /,/, $_;
                        for (@time) {
                                if (/[0-9]{2}:/) {
                                        $newTime .= $obj->_getFormatedTime($_);
                                } elsif(/[0-9]{3}/) {
                                        my $aux = ",$_ --> ";
                                        $aux = substr $aux, 0, -5 unless $addArrow ;
                                        $newTime .= $aux;
                                        $addArrow = 0;
                                }
                        }
                }
               
                return $newTime;
        }
       
        # Devuelve el string de tiempo en formato HH:MM:SS
        sub _getFormatedTime {
                my $obj = shift;
               
                my @time = split /:/, $_[0];
               
                # Obtenemos cantidad de segundos
                my $seconds = @time[2]; # Segundos
                $seconds += @time[1]*60; # min a segundos
                $seconds += @time[0]*3600; # hora a segundos
               
                # Sumamos (segundos finales)
                $seconds = $seconds + $obj->{-desvio};
               
                my ($h, $restMin) =  (int($seconds / 3600), $seconds % 3600);
                my ($m, $s)                             =  (int($restMin / 60), $restMin % 60);
                $h = sprintf "%.2d", $h;
                $m = sprintf "%.2d", $m;
                $s = sprintf "%.2d", $s;
               
                return "$h:$m:$s";
        }
       
1;
 
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


subs_desvia.pl
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl

# Desviador de tiempo. Subtitulos.
# primer parametro: path_al_archivo_srt
# segundo parametro: tiempo a desviar => ej.:
        # 45
        # 15m
        # 1h
        # -10
        # -15m
# 45 => son segundos, 15m => son 15 minutos, 1h => 1 hora

@INC = ("./");

use strict;
use Subtitle;

# Validamos numero de argumentos.
die "Debe ingresar el fichero de subtitulos\n"  unless $ARGV[0];
die "Debe ingresar el tiempo de desvío\n"                      unless $ARGV[1];

my $sub = Subtitle->new(); # Creamos.
$sub->loadFile($ARGV[0]); # Cargamos archivo.
$sub->loadDesvio($ARGV[1]); # Cargamos tiempo de desvio.
$sub->makeBackup(); # Hacemos un backup del original.
$sub->generate(); # Generamos el archivo.
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
gaston
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2008-01-28 22:16 @969


Volver a Novedades en Perl en Español

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado