[英]convert C# code to powershell: scriptblock-delegate
這是我想轉換為“powershell方式”的代碼:
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)
但它不起作用。 感謝您的幫助。
我知道我必須使用 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 用單個 \\n 表示此處字符串中的行尾。
此外,如上所述,字符串末尾沒有 \\n,因為此處字符串的末尾由 \\n'@ 標記(即行首的 '@),因此最后一個 \\n 是一部分結束標記,而不是字符串。
不幸的是,僅僅刪除無關的 \\r 和 \\n 字符轉義是行不通的。 沒有具體的匹配來定義 <text> 在哪里結束, [\\s\\S]*? 將匹配為空(讓整個模式成功的最小匹配)。 要匹配所有 <text>,請使用 [\\s\\S]*(貪婪版本)或使用 [\\s\\S]*?$ 強制匹配到字符串的末尾。
此外,: 和 > 不是元字符(如 . 或 *),因此不需要轉義(盡管它不會造成傷害)。 基思希爾修復了這個問題,但沒有提到它。 此外,指定 MultiLine 選項標志 (?m) 沒有任何意義,因為原始模式沒有受影響的錨點(^ 和 $)並且 C# 版本無論如何都沒有設置它。 即使 <text> 捕獲組使用 [\\s\\S]*?$,這個 $ 也匹配字符串的結尾,而不是中間的 \\n(盡管如果存在,它會留下不匹配的終止 \\n)。 因此(修復而不是替換)正則表達式應該是:
(?<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\\n 作為行尾標記。
雖然我會說 Keith Hill 使用 \\s*$\\s* 來匹配行尾的答案更可靠,因為它同時匹配 \\n 和 \\r\\n(以及任何后面的或尚未匹配的前面的空格),如果文件的結構是已知和固定的,然后使用無界量詞來匹配固定部分可能會導致細微的錯誤。 在這種情況下,使用 \\s*$\\s* 匹配 <end> 和 <text> 捕獲組之間的 \\r\\n 將導致 <text> 開頭的任何空格被丟棄。 如果行尾標記只能是 \\n 或 \\r\\n 那么 \\r?\\n 更安全。
此外,使用 .*$\\s*.*$ 意味着 <text> 匹配一個(可能為空)行,后跟任意數量的行(包括 0)只包含 0 個或多個空格,后跟一個(可能為空)線。 雖然這適用於正則表達式解析具有 2 個 <text> 行的單個示例條目的原始問題,但該文件可能包含許多條目。 通過參考最后包含 \\r\\n\\r\\n 的原始(並且可能有效)C# 版本,看起來 <text> 可以有任意數量的行,並且條目由空行分隔。 這也可以解釋使用“懶惰”模式 [\\s\\S]*?\\r\\n\\r\\n 來捕獲 <text> 直到(並包括)下一個空行而不是捕獲所有內容最后一個空行(貪婪的 [\\s\\S]*\\r\\n\\r\\n)。
因此,“工作代碼”模式可能應該是:
(?<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)
IE 只是 C# 版本,沒有轉義:或 >。 因此,cool25 犯的基本錯誤是將測試字符串存儲在 powershell here-string 中,從而對其進行更改,使其不再代表要解析的實際數據。 這里的教訓(除了實際的編程)是,在為例程創建測試數據時,請確保測試數據的來源與實際數據的來源盡可能相似。 在這種情況下,由於該例程旨在處理包含多個條目的文件,因此最好的測試數據應該是包含一個條目的文件。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.