可以将特定数字添加到一堆“时间”字符串中,在正则表达式中完成

Posted

技术标签:

【中文标题】可以将特定数字添加到一堆“时间”字符串中,在正则表达式中完成【英文标题】:Can adding a particular number to a bunch of "time" strings, be done in Regex 【发布时间】:2015-08-19 19:12:31 【问题描述】:

我有一个“srt”文件(如标准电影字幕格式),如下链接所示:http://pastebin.com/3k8a53SC

摘录:

1
00:00:53,000 --> 00:00:57,000
<any text that may span multiple lines>

2
00:01:28,000 --> 00:01:35,000
<any text that may span multiple lines>

但是现在的字幕时间都错了,落后了 9 秒。

是否可以在每次使用正则表达式时添加 9 秒(+9)? 即使毫秒设置为 000 也可以,但增加 9 秒应遵守“60 秒 = 1 分钟 & 60 分钟 = 1 小时”规则。 此外,计时输入后的字幕文本不得被正则表达式更改。

顺便说一下,每个时间字符串的时间格式是“Hours:Minutes:Seconds.Milliseconds”。

【问题讨论】:

您使用什么语言或工具? 【参考方案1】:

快速回答是“不”,这不是正则表达式的应用程序。正则表达式允许您匹配文本,但不能更改它。更改内容超出了正则表达式本身的范围,并且属于您使用的语言——perl、awk、bash 等。

对于在 SRT 文件中调整时间的任务,您可以在 bash 中轻松完成,使用 date 命令调整时间。

#!/usr/bin/env bash

offset="$1:-0"

datematch="^(([0-9]2:)2[0-9]2),[0-9]3 --> (([0-9]2:)2[0-9]2),[0-9]3"

os=$(uname -s)

while read line; do
  if [[ "$line" =~ $datematch ]]; then

    # Gather the start and end times from the regex
    start=$BASH_REMATCH[1]
    end=$BASH_REMATCH[3]

    # Replace the time in this line with a printf pattern
    linefmt="$line//[0-2][0-9]:[0-5][0-9]:[0-5][0-9]/%s\n"

    # Calculate new times
    case "$os" in
      Darwin|*BSD)
        newstart=$(date -v$offsetS -j -f "%H:%M:%S" "$start" '+%H:%M:%S')
        newend=$(date -v$offsetS -j -f "%H:%M:%S" "$end" '+%H:%M:%S')
        ;;
      Linux)
        newstart=$(date -d "$start today $offset seconds" '+%H:%M:%S')
        newend=$(date -d "$end today $offset seconds" '+%H:%M:%S')
        ;;
    esac

    # And print the result
    printf "$linefmt" "$newstart" "$newend"

  else
    # No adjustments required, print the line verbatim.
    echo "$line"
  fi
done

注意case 语句。此脚本应针对 Linux、OSX、FreeBSD 等自动调整。

你会像这样使用这个脚本:

$ ./srtadj -9 < input.srt > output.srt

当然,假设您是这样命名的。或者更有可能的是,您会调整其逻辑以在您自己的脚本中使用。

【讨论】:

【参考方案2】:

不,抱歉,你不能。正则表达式是一种上下文无关的语言(参见 Chomsky,例如 https://en.wikipedia.org/wiki/Chomsky_hierarchy),您无法计算。 但是对于像 perl 这样的上下文敏感语言,它会起作用。 它可能是像这样的单班轮;-)))

perl -n -e 'if(/^(\d\d:\d\d:\d\d)([-,\d\s\>]*)(\d\d:\d\d:\d\d)(.*)/) print plus9($1).$2.plus9($3).$4."\n";elseprint $_  sub plus9 ($h,$m,$s)=split(/:/,shift); $t=(($h*60+$m)*60+$s+9); $h=int($t/3600);$r=$t-($h*3600);$m=int($r/60);$s=$r-($m*60);return sprintf "%02d:%02d:%02d", $h, $m, $s;‘ movie.srt

与 move.srt 类似

1
00:00:53,000 --> 00:00:57,000
hello

2
00:01:28,000 --> 00:01:35,000
I like perl

3
00:02:09,000 --> 00:02:14,000
and regex

你会得到

1
00:01:02,000 --> 00:01:06,000
hello

2
00:01:37,000 --> 00:01:44,000
I like perl

3
00:02:18,000 --> 00:02:23,000
and regex

如果您想要另一个增量,您可以更改“sub plus9...”中的 +9。

它是如何工作的? 我们正在寻找匹配的行 dd:dd:dd something dd:dd:dd something 然后我们调用一个 sub,它将匹配的第一组 ($1) 和第三组 ($3) 添加 9 秒。所有其他行都保持不变。

添加 如果你想把 perl oneliner 放在一个文件中,比如 plus9.pl,你可以添加换行符 ;-)

if(/^(\d\d:\d\d:\d\d)([-,\d\s\>]*)(\d\d:\d\d:\d\d)(.*)/) 
    print plus9($1).$2.plus9($3).$4."\n";
 else 
    print $_


sub plus9
    ($h,$m,$s)=split(/:/,shift);
    $t=(($h*60+$m)*60+$s+9);
    $h=int($t/3600);
    $r=$t-($h*3600);
    $m=int($r/60);
    $s=$r-($m*60);
    return sprintf "%02d:%02d:%02d", $h, $m, $s;

【讨论】:

感谢您的回复,我有 Windows 7 电脑,当我在 cmd 中运行上述命令时,它会给出消息“文件名、目录名或卷标语法不正确”(perl 已安装并在我的ENV PATH 已经)。那么你可以为 Windows 7 更新这个命令吗? 嗯,你用命令 perl -V 得到了什么?它有效吗?你应该得到 perl 的版本信息。 是的,正如我告诉你的那样,perl 已安装并使用 ENV PATH 设置,当我运行“perl -v”时,我得到的版本号是 5.20.something 在我的情况下。 我现在没有窗户,也许明天。我认为“d:”被误解了。您可以在 perl 选项参数周围使用双引号 " 而不是单引号 ' 吗?但是请注意,您必须转义内部的双引号。或者,您可以将其写在文件中(不带引号),比如 plus9.pl 和然后调用 perl -n plus9.pl movie.srt 谢谢,我会在windows系统上试试。【参考方案3】:

正则表达式严格匹配,不能加减。您可以使用 python 匹配每个日期时间字符串,例如,添加 9 秒,然后在适当的位置重写字符串。我用来匹配它的正则表达式如下:

(?<hour>\d+):(?<minute>\d+):(?<second>\d+),(?<msecond>\d+)

它标记了捕获组,因此很容易获取每个部分(您不需要msecond,但我猜它是用于可视化的)

Regex101

【讨论】:

以上是关于可以将特定数字添加到一堆“时间”字符串中,在正则表达式中完成的主要内容,如果未能解决你的问题,请参考以下文章

如何有条件地将前导零添加到一列数字?

如何使用正则表达式从数据框中分离数字?

如何显示特定的VC,更像是导航到一堆VC

使用 pyspark 中的正则表达式将数字添加到字符串中最后一个字符之前的字符串

C ++在字符串中查找特定数字

求一个正则表达式: 以英文字母开头,只能包含英文字母、数字、下划线