You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
3.6 KiB
171 lines
3.6 KiB
package Subtitle::Utils; |
|
|
|
use strict; |
|
use warnings; |
|
|
|
use base 'Exporter'; |
|
|
|
our @EXPORT_OK = qw( |
|
chomp_all strip_bom trim |
|
make_timing parse_timeshift parse_timing |
|
round |
|
); |
|
|
|
our %EXPORT_TAGS = ( |
|
string => [qw(chomp_all strip_bom trim)], |
|
timing => [qw(make_timing parse_timeshift parse_timing)], |
|
); |
|
|
|
## string functions |
|
|
|
sub chomp_all { |
|
return unless $_[0]; |
|
$_[0] =~ s/[\r\n]+$//o; |
|
} |
|
|
|
sub strip_bom { |
|
return unless @_; |
|
return $_[0] =~ s/^\xEF\xBB\xBF//o; |
|
} |
|
|
|
sub trim { |
|
return unless $_[0]; |
|
$_[0] =~ s/(^\s+|\s+$)//go; |
|
} |
|
|
|
## timing functions |
|
|
|
sub make_timing { |
|
my ($time) = @_; |
|
my ($hrs, $min, $sec, $msec, $rest); |
|
$hrs = int($time / 3600); |
|
$rest = $time - ($hrs * 3600); |
|
$min = int($rest / 60); |
|
$rest = $rest - ($min * 60); |
|
$sec = int($rest / 1); |
|
$msec = sprintf "%.0f", (($rest - $sec) * 1000); |
|
return ($hrs, $min, $sec, $msec); |
|
} |
|
|
|
# recognized format [+-][[hh:]mm:]ss[.ms] |
|
sub parse_timeshift { |
|
my ($str) = @_; |
|
my $ts = { sign => '+', hrs => 0, min => 0, sec => 0 }; |
|
|
|
return unless defined $str; |
|
|
|
while(1) { |
|
if ($str =~ s<^([+-])><>o) { $ts->{sign} = $1; } |
|
if ($str =~ s<^([0-9](?:\.\d{1,3})?)$><>o) { $ts->{sec} = $1; } |
|
if ($str =~ s<([0-5][0-9](?:\.\d{1,3})?)$><>o) { $ts->{sec} = $1; } |
|
if ($str =~ s<^([0-9]):$><>o) { $ts->{min} = $1; } |
|
if ($str =~ s<([0-5][0-9]):$><>o) { $ts->{min} = $1; } |
|
if ($str =~ s<^(\d+):$><>o) { $ts->{hrs} = $1; } |
|
last; |
|
}; |
|
|
|
if ($str or $ts->{min} >= 60 or $ts->{sec} >= 60.0) { |
|
return; # wrong time format |
|
} |
|
my $time = $ts->{sec} + $ts->{min} * 60 + $ts->{hrs} * 3600; |
|
$time *= -1 if $ts->{sign} eq '-'; |
|
return $time; |
|
} |
|
|
|
sub parse_timing { |
|
my ($str) = @_; |
|
my $time = 0.0; |
|
return unless $str =~ m/(\d+):(\d+):(\d+)[.,](\d+)/o; |
|
my ($hrs, $min, $sec, $msec) = ($1, $2, $3, $4); |
|
if ($msec < 0 or $msec > 999) { |
|
return -1; # wrong mseconds part of timing |
|
} |
|
if ($sec < 0 or $sec > 59) { |
|
return -1; # wrong seconds part of timing |
|
} |
|
if ($min < 0 or $min > 59) { |
|
return -1; # wrong minutes part of timing |
|
} |
|
if ($hrs < 0) { |
|
return -1; # wrong minutes part of timing |
|
} |
|
my $msec_len = length $msec; |
|
if ($msec_len == 3) { $time += $msec * 0.001; } |
|
elsif ($msec_len == 2) { $time += $msec * 0.01; } |
|
elsif ($msec_len == 1) { $time += $msec * 0.1; } |
|
else { return -1; } # abnormal length of mseconds part |
|
$time += $sec; |
|
$time += $min * 60; |
|
$time += $hrs * 60 * 60; |
|
return $time; |
|
} |
|
|
|
## misc |
|
|
|
sub round { |
|
my ($value, $n) = @_; |
|
return sprintf "%.${n}f", $value; |
|
} |
|
|
|
1; |
|
|
|
=pod |
|
|
|
=head1 NAME |
|
|
|
Subtitle::Utils -- usefull generic routines |
|
|
|
=head1 SYNOPSYS |
|
|
|
use Subtitle::Utils qw(:all); |
|
|
|
=head1 FUNCTIONS / STRINGS |
|
|
|
use Subtitle::Utils qw(:string); |
|
|
|
=head2 C<chomp_all> |
|
|
|
chomp($line); |
|
|
|
In-place strips newlines (CR/LF) from line. |
|
Differs from standart chomp() than strips '\r', '\n' and it's combinaion, disregarding $/ |
|
|
|
=head2 C<strip_bom> |
|
|
|
strip_bom($line); |
|
|
|
In-place strips Unicode's BOM (Byte Order Mark) from line. |
|
|
|
=head2 C<trim> |
|
|
|
trim($line); |
|
|
|
In-place strips leading and trailing spaces from the given string |
|
|
|
=head1 FUNCTIONS / TIMING |
|
|
|
=head2 C<make_timing> |
|
|
|
my ($hrs, $min, $sec, $msec) = make_timing($time); |
|
printf "%d:%02d:02d.%s", $hrs, $min, $sec, $msec; |
|
|
|
Takes float number of seconds and returns array with components of timing split by units. |
|
|
|
=head2 C<parse_timing> |
|
|
|
my $time = parse_timing($string); |
|
|
|
Takes string like "HH:MM:SS.MSEC" and returns float number of seconds |
|
|
|
On parse error returns -1 |
|
|
|
=head1 FUNCTIONS / MISC |
|
|
|
=head2 C<round> |
|
|
|
say round(3.6, 0); # 4 |
|
say round(3.6, 1); # 4.1 |
|
|
|
Self-descriptive |
|
|
|
=cut
|
|
|