批处理 - 如何对时间码执行数学运算?

时间:2016-06-21 19:59:04

标签: batch-file awk sed cygwin gnu

我一直在处理批处理脚本,要更改一些.xml文件格式/类型。

这几乎已经完成,但我在更改时间码方面遇到了问题。

这是一个例子

  <Events>
    <Event In="00:00:20.650" Out="00:00:22.970"
    <Event In="00:00:23.050" Out="00:00:26.300"

此时间格式hh:mm:ss.ms应更改为hh:mm:ss:ff

表示将Milliseconds更改为Frames

执行此操作的公式如下:ms*25/1000或简单ms*0.025

输出应为

  <Events>
    <Event In="00:00:20:16" Out="00:00:22:24"
    <Event In="00:00:23:01" Out="00:00:26:08"

备注

  • 0.025是每毫秒的帧数。
  • 帧数必须是任何小数分量的整数。
  • 我正在使用GNUWinCygwin库工具,因此可以使用bc exprtrawk {{ 1}}或任何解决方案来完成工作。

我不确定这是否有很多要问,但任何形式的帮助都会很棒。

最好的问候

4 个答案:

答案 0 :(得分:2)

您可以使用awk脚本执行此操作:

<强> script.awk

{ 
  re = "[0-9]{2}:[0-9]{2}:[0-9]{2}.([0-9]{3})"
  while( match( $0, re, grps) ) {
    frames = sprintf("%02.0f",( grps[1] *0.025 ) )
    gsub( "." grps[1], ":" frames)
  }
  print
}

awk -f script.awk yourfile一样运行它。

它尝试匹配这样的时间戳并将最后一部分(毫秒)捕获到grps[1]。然后计算完成,sprintf用于格式化帧。

之后gsub用帧替换毫秒。

答案 1 :(得分:2)

<强>的PowerShell:

[xml]$xml = gc events.xml

function fix([timespan]$ts) {
    "{0}:{1:D2}" -f $ts.ToString("hh\:mm\:ss"),[int]($ts.Milliseconds * 0.025)
}

$xml.SelectNodes('//Event') | %{
    $_.In = fix $_.In
    $_.Out = fix $_.Out
}

$xml.Save("events_fixed.xml")

如果您希望将其作为批处理脚本,可以将其转换为Batch + PowerShell多语言,方法是将其插入顶部并使用.bat扩展名保存:

<# : batch portion
@echo off & setlocal

powershell "iex (${%~f0} | out-string)"
goto :EOF

: end Batch / begin PowerShell hybrid code #>

答案 2 :(得分:2)

这是一个纯粹的解决方案。此脚本对于cmd具有特殊含义的所有字符都是安全的。基本上,它会在给定的输入文件中搜索关键字或属性名称InOut,然后搜索=以及用引号""括起来的时间代码。如果找到,则将毫秒部分拆分,转换为帧并附加到剩余时间码,由:分隔。每行可以有任意数量的时间码,只要它们的格式与样本数据中给出的格式相同,并且其中一个给定的关键字在前面,就可以识别和转换所有时间码。

该脚本支持舍入转换后的值 - 请参阅文件开头的变量定义rem处的备注(FORMULA)(在带有备注{{1的块中查找) }})。

这是代码:

Define constants here:

假设脚本保存为@echo off setlocal EnableExtensions DisableDelayedExpansion rem // Define constants here: set KEYWORDS="In","Out" & rem // (provide a list of attribute names) set "PATTERN=[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9]" set /A PLENGTH=12 & rem // (char. length of resolved `PATTERN` string) set "FORMULA=(MS*25+0)/1000" & rem /* (`+0` means to round frames down; rem use `+999` instead to round up, or `+500` to round to nearest) */ set /A PADDING=2 & rem // (number of digits of resulting frame value) set "FILE=%~1" if not defined FILE exit /B 1 for /F "delims=" %%L in ('findstr /N /R "^" "%FILE%"') do ( set "LINE=%%L" setlocal EnableDelayedExpansion set "LINE=!LINE:*:=!" if defined LINE ( for /F delims^=^ eol^= %%E in ("!LINE!") do ( endlocal set "LINE=%%E" setlocal EnableDelayedExpansion ) ) else ( endlocal set "LINE=" setlocal EnableDelayedExpansion ) for %%P in (!KEYWORDS!) do ( endlocal set "KEYWORD=%%~P" call :PROCESS LINE LINE KEYWORD setlocal EnableDelayedExpansion ) echo(!LINE! endlocal ) endlocal exit /B :PROCESS rtn_string ref_string ref_key setlocal EnableDelayedExpansion set "COLL=" set "LINE=!%~2!" set "KEYW=!%~3!" :LOOP if defined LINE ( call :LEN LLENGTH LINE call :LEN KLENGTH KEYW for /F delims^=^ eol^= %%T in ("!KEYW!") do ( set "SEEK=!LINE:*%%T=!" ) if defined SEEK ( if not "!SEEK!"=="!LINE!" ( call :LEN SLENGTH SEEK set /A TLENGTH=LLENGTH-^(SLENGTH+KLENGTH^) for /F "tokens=1,2" %%M in ("!TLENGTH! !KLENGTH!") do ( set "DONE= !LINE:~,%%M!" set "TEST=!LINE:~%%M,%%N!" ) set "CHAR=!DONE:~-1!" & set "CHAR=!CHAR: = !" set "DONE=!DONE:~1!" if "!TEST!"=="!KEYW!" ( if "!CHAR!"==" " ( set "DONE=!DONE!!KEYW!" set "TEST=!SEEK!" set /A TLENGTH=PLENGTH+3 for /F %%N in ("!TLENGTH!") do ( set "TEST=!SEEK:~,%%N!" & set "SEEK=!SEEK:~%%N!" ) echo(!TEST! | > nul findstr /R /C:^^^"^^^^^^^=\"!PATTERN!\"^^^ $^^^" if not ErrorLevel 1 ( for /F "tokens=1,2 delims=." %%S in ("!TEST:~1!") do ( set "HHMMSS=%%~S" & set "MS=%%~T" set /A "FF=%FORMULA%" set "FF=0000!FF!" & set "FF=!FF:~-%PADDING%!" ) set "COLL=!COLL!!DONE!="!HHMMSS!:!FF!"" & set "LINE=!SEEK!" ) else ( set "COLL=!COLL!!DONE!!TEST!" & set "LINE=!SEEK!" ) ) else ( set "COLL=!COLL!!DONE!!TEST!" & set "LINE=!SEEK!" ) ) else ( set "COLL=!COLL!!DONE!!TEST!" & set "LINE=!SEEK!" ) ) else ( set "COLL=!COLL!!LINE!" & set "LINE=" ) ) else ( set "COLL=!COLL!!LINE!" & set "LINE=" ) goto :LOOP ) if defined COLL ( for /F delims^=^ eol^= %%E in ("!COLL!") do ( endlocal set "%~1=%%E" setlocal EnableDelayedExpansion ) ) else ( endlocal set "%~1=" setlocal EnableDelayedExpansion ) endlocal exit /B :LEN rtn_length ref_string setlocal EnableDelayedExpansion set "STR=!%~2!" if not defined STR (set /A LEN=0) else (set /A LEN=1) for %%L in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do ( if defined STR ( set "INT=!STR:~%%L!" if not "!INT!"=="" set /A LEN+=%%L & set "STR=!INT!" ) ) endlocal & set "%~1=%LEN%" exit /B ,输入文件名为convert-ms-to-frames.bat,输出文件名为sample.xml,请使用以下命令行:

return.xml

只需删除convert-ms-to-frames.bat "sample.xml" > "return.xml" 部分即可在控制台上显示输出数据以进行测试。

假设输入文件> "return.xml"包含以下数据......:

sample.xml

...输出文件<?xml version="1.0"?> <Events> <Event In="00:00:20.650" Out="00:00:22.970" /> <Event In="00:00:23.050" Out="00:00:26.300" /> </Events> 将包含以下数据:

return.xml

答案 3 :(得分:1)

使用bc

$ ms="650"
$ ff=$(echo ${ms} "*0.025/1" |bc)
$ echo $ff
16

当结果小于10时我留给你,以添加额外的“0”