将 C# 代码转换为 powershell:scriptblock-delegate

本文关键字:powershell scriptblock-delegate 转换 代码 | 更新日期: 2023-09-27 18:03:30

这是我想要转换为"PowerShell Way"的代码:

private static Regex unit = new Regex(
        @"(?<sequence>'d+)'r'n(?<start>'d{2}':'d{2}':'d{2},'d{3}) --'> (?<end>'d{2}':'d{2}':'d{2},'d{3})'r'n(?<text>['s'S]*?'r'n'r'n)", 
        RegexOptions.Compiled | RegexOptions.ECMAScript);

    output.Write(
        unit.Replace(input.ReadToEnd(), delegate(Match m)
        {
            return m.Value.Replace(
                String.Format("{0}'r'n{1} --> {2}'r'n",
                    m.Groups["sequence"].Value,
                    m.Groups["start"   ].Value,
                    m.Groups["end"     ].Value),
                String.Format(
                    "{0}'r'n{1:HH'':mm'':ss'',fff} --> " + 
                    "{2:HH'':mm'':ss'',fff}'r'n",informatifetcourrier.com   CuImE
                    sequence++,
                    DateTime.Parse(m.Groups["start"].Value.Replace(",","."))
                            .AddSeconds(offset),
                    DateTime.Parse(m.Groups["end"  ].Value.Replace(",","."))
                            .AddSeconds(offset)));

而我的尝试:

$text=@'
2
00:00:03,601 --> 00:00:06,603
<i>Vous devrez trouver quelqu'un
qui pense différemment pour l'attraper.</i>
'@
$regex ='(?m)(?<sequence>'d+)'r'n(?<start>'d{2}':'d{2}':'d{2},'d{3}) --'> (?<end>'d{2}':'d{2}':'d{2},'d{3})'r'n(?<text>['s'S]*?'r'n'r'n)'
$r = New-Object System.Text.RegularExpressions.Regex $regex
$MatchEvaluator = 
{  
    param($m) 
    $m.value.replace([string]::Format("{0}'r'n{1} --> {2}'r'n",
        $m.Groups["sequence"].Value,
        $m.Groups["start"   ].Value,
        $m.Groups["end"     ].Value),
    [string]::Format("{0}'r'n{1:HH'':mm'':ss'',fff} --> {2:HH'':mm'':ss'',fff}'r'n",
        [datetime]::Parse($m.Groups["start"].Value.Replace(",",".")).AddSeconds(1),
        [datetime]::Parse($m.Groups["end"  ].Value.Replace(",",".")).AddSeconds(1)))
}
$result = $r.Replace($text, $MatchEvaluator)

但它不起作用。谢谢你的帮助。

我知道我必须使用脚本块委托来达到我的目的。

将 C# 代码转换为 powershell:scriptblock-delegate

你在这里遇到了几个问题。 这是一个有效的版本:

$text=@'
2
00:00:03,601 --> 00:00:06,603
<i>Vous devrez trouver quelqu'un
qui pense différemment pour l'attraper.</i>
'@
$regex = [regex]'(?m)(?<sequence>'d+)'s*$'s*(?<start>'d{2}:'d{2}:'d{2},'d{3}) --> (?<end>'d{2}:'d{2}:'d{2},'d{3})'s*$'s*(?<text>.*$'s*.*$)'
$MatchEvaluator = {  
    param($m) 
    $oldValue = "{0}`r`n{1} --> {2}`r`n" -f $m.Groups["sequence"].Value,
                    $m.Groups["start"].Value, $m.Groups["end"].Value
    $seq   = 5 + $m.Groups["sequence"].Value
    $start = ([DateTime]$m.Groups["start"].Value.Replace(",",".")).AddSeconds(1)
    $end   = ([DateTime]$m.Groups["end"].Value.Replace(",",".")).AddSeconds(1)
    $newValue = "{0}`r`n{1:HH:mm:ss,fff} --> {2:HH:mm:ss,fff}`r`n" -f $seq,$start,$end
    $m.value.replace($oldValue, $newValue)
}
$result = $regex.Replace($text, $MatchEvaluator)
$result

首先,在PowerShell双引号字符串中,`r`n用于CRLF。 其次,您缺少上面$seq替换字符串的参数。 第三,您不需要转义正则表达式中的:。第四,-f算子是上面的包装器,使用起来比[String]::Format()更方便。

这输出:

7
00:00:04,601 --> 00:00:07,603
<i>Vous devrez trouver quelqu'un
qui pense différemment pour l'attraper.</i>

我不知道你想如何修改序列号,所以我只是在上面添加了 5。

非常感谢

基思·希尔。这是我的工作代码:

$file = "D:'subtitles'Hannibal - 02x10 - eng.srt"
$text =get-content($file) -Raw     # powershell V3
Write-Output "offset, in seconds (+1,1, -2,75):"
[Double]$offset = 0
while(![Double]::TryParse((Read-host),[ref] $offset))
{
Write-Output "Not a Number. Do again"
 }
$regex = [regex]'(?m)(?<sequence>'d+)'s*$'s*(?<start>'d{2}:'d{2}:'d{2},'d{3}) --> (?<end>'d{2}:'d{2}:'d{2},'d{3})'s*$'s*(?<text>.*$'s*.*$)'
$MatchEvaluator = {  
param($m) 
$oldValue = "{0}`r`n{1} --> {2}`r`n" -f $m.Groups["sequence"].Value,
                $m.Groups["start"].Value, $m.Groups["end"].Value
$seq   =+$m.Groups["sequence"].Value
$start = ([DateTime]$m.Groups["start"].Value.Replace(",",".")).AddSeconds($offset)
$end   = ([DateTime]$m.Groups["end"].Value.Replace(",",".")).AddSeconds($offset)
$newValue = "{0}`r`n{1:HH:mm:ss,fff} --> {2:HH:mm:ss,fff}`r`n" -f $seq, $start,$end
$m.value.replace($oldValue, $newValue)
}
$result = $regex.Replace($text, $MatchEvaluator) | out-file -Encoding utf8 "D:'subtitles'Hannibal - 02x10 - eng_offset_$offset.srt"

对我来说,下一步是合并英语和法语字幕(例如:70%的法语和30%的英语(。欢迎任何建议。

用于教学目的的一些细节:

mjolinor 的评论是正确的,因为正则表达式确实不正确,因为 powershell 用单个 ' 表示 here-string 中的行尾。

此外,如上所述,字符串末尾没有 ',因为 here-string 的末尾由 ''@ 标记(即行首的 '@(,因此最后一个 ' 是结束标记的一部分,而不是字符串。

不幸的是,仅删除无关的 ''r 和 ' 字符转义是行不通的。如果没有具体的匹配来定义<文本>的结束位置,[''s''S]*?将匹配为空(让整个模式成功的最小匹配(。若要匹配所有<文本>请使用 [''s''S]*(贪婪版本(或使用 [''s''S]*?$ 强制匹配到字符串的末尾。

此外,: 和> 不是元字符(如 . 或 *(,因此不需要转义(尽管它没有伤害(。基思·希尔(Keith Hill(修复了这个问题,但没有提及。此外,指定多行选项标志 (?m( 没有任何用途,因为原始模式没有受影响的锚点(^ 和 $(,并且 C# 版本无论如何都没有设置它。即使 捕获组使用 [''s''S]*?$,此 $ 也匹配字符串的末尾,而不是中间 '(尽管如果存在,它将使终止 ' 不匹配(。因此,(修复而不是替换的(正则表达式应该是:

 (?<sequence>'d+)'n(?<start>'d{2}:'d{2}:'d{2},'d{3}) --> (?<end>'d{2}:'d{2}:'d{2},'d{3})'n(?<text>['s'S]*)

注意:此解释源自原始问题。随后发布的"工作代码"显示$text值是从文件中获取(原始(的,因此可能包含''r'作为行尾标记。

虽然我会说 Keith Hill 使用 ''s*$''s* 匹配行尾的答案更健壮,因为它匹配 ' 和 ''r'(以及任何后续或尚未匹配的前一个空格(,但如果文件的结构是已知且固定的,那么使用无界量词来匹配固定部分可能会导致细微的错误。在这种情况下,使用 ''s*$''s* 匹配 之间的 ''r' 捕获组将导致丢弃开头的任何空格。如果行尾标记只能是 ' 或 ''r',则 ''r?' 更安全。

此外,使用 .*$''s*.*$ 意味着 匹配一个(可能为空的(行,后跟

任意数量的行(包括 0(,该行仅包含 0 个或多个空格,后跟一个(可能为空的(行。虽然这适用于正则表达式解析具有 2 <文本>行的单个示例条目的原始问题,但该文件可能包含许多条目。通过引用末尾包含 ''r'''r' 的原始(可能有效(C# 版本,似乎可以有任意数量的行,并且条目由空行分隔。这也解释了为什么使用"懒惰"模式 [''s''S]*?''r'''r' 来捕获<文本>直到(并包括(下一个空白行,而不是捕获最后一个空白行之前的所有内容(贪婪 [''s''S]*''r'''r'(。

因此,"工作代码"模式可能应该是:

  (?<sequence>'d+)'r'n(?<start>'d{2}:'d{2}:'d{2},'d{3}) --> (?<end>'d{2}:'d{2}:'d{2},'d{3})'r'n(?<text>['s'S]*?'r'n'r'n)

即只是 C# 版本,无法转义 : 或>。因此,cool25 犯的基本错误是将测试字符串存储在 powershell here 字符串中,从而对其进行更改,使其不再表示要解析的实际数据。这里的教训(除了实际编程之外(是,在为例程创建测试数据时,请确保测试数据源尽可能与实际数据源相似。在这种情况下,由于例程旨在处理包含多个条目的文件,因此最好的测试数据将是一个条目的文件。