SRT 是众所周知的字幕文本文件格式。以下是此格式的简单示例:
1
00:05:00,400 --> 00:05:15,300
This is an example of
a subtitle.
2
00:05:16,400 --> 00:05:25,300
This is an example of
a subtitle - 2nd subtitle.
有时,不幸的是,下载的字幕文件与购买的电影不同步。如何用 C# 编写一个简单的实用程序,将时间平移应用于现有的字幕文件?
以下 .NET 8 控制台应用程序演示了在 .srt 文件上应用时移的最小工作版本:
var seconds = TimeSpan.FromSeconds(3);
string inputFile = @"d:\how to train your dragon.srt";
string outputFile = @"d:\how to train your dragon shifted.srt";
File.WriteAllLines(outputFile,
from line in File.ReadAllLines(inputFile)
let duration = Duration.ParseOrNull(line)
select duration is null ? line : duration.Shift(seconds).ToString());
public record Duration(TimeSpan Start, TimeSpan End)
{
private const string FormatString = @"hh\:mm\:ss\,fff";
private static readonly System.Globalization.CultureInfo Culture = new("en");
public static Duration? ParseOrNull(string line)
{
if (!line.Contains(" --> ")) return null;
string[] duration = line.Split(new[] { " --> " }, StringSplitOptions.None);
return new Duration(
Start: TimeSpan.ParseExact(duration[0], FormatString, Culture),
End: TimeSpan.ParseExact(duration[1], FormatString, Culture));
}
public Duration Shift(TimeSpan shift) => new(Start.Add(shift), End.Add(shift));
public override string ToString() => $"{ToDuration(Start)} --> {ToDuration(End)}";
private static string ToDuration(TimeSpan value) => value.ToString(FormatString);
}
缺少示例
以下类表示具有
Parse
、Shift
和 ToLines
方法的单个(多行)字幕条目:
[DebuggerDisplay("{Number} {Start} --> {End} {Lines[0]}")]
public sealed class Subtitle
{
private const string FormatString = @"hh\:mm\:ss\,fff";
public Subtitle(int number, TimeSpan start, TimeSpan end, string[] lines)
{
Number = number;
Start = start;
End = end;
Lines = lines;
}
public int Number { get; }
public TimeSpan Start { get; }
public TimeSpan End { get; }
public string[] Lines { get; }
public static IEnumerable<Subtitle> Parse(string[] lines)
{
int index = 0;
while (index < lines.Length)
{
string numberLine = lines[index++];
string durationLine = lines[index++];
var subtitleLines = new List<string>();
string subtitleLine = lines[index++];
do
{
subtitleLines.Add(subtitleLine);
subtitleLine = index < lines.Length ? lines[index++] : "";
}
while (subtitleLine != "");
var subtitle = Subtitle.Parse(numberLine, durationLine, subtitleLines);
yield return subtitle;
}
}
private static Subtitle Parse(string numberLine, string durationLine, IEnumerable<string> subtitleLines)
{
int number = int.Parse(numberLine);
string[] duration = durationLine.Split(new[] { " --> " }, StringSplitOptions.None);
TimeSpan start = TimeSpan.ParseExact(duration[0], FormatString, CultureInfo.InvariantCulture);
TimeSpan end = TimeSpan.ParseExact(duration[1], FormatString, CultureInfo.InvariantCulture);
return new Subtitle(number, start, end, subtitleLines.ToArray());
}
public Subtitle Shift(TimeSpan duration) =>
new Subtitle(this.Number, this.Start.Add(duration), this.End.Add(duration), this.Lines);
public IEnumerable<string> ToLines()
{
yield return this.Number.ToString();
yield return $"{this.ToDuration(this.Start)} --> {this.ToDuration(this.End)}";
foreach (var line in this.Lines)
{
yield return line;
}
yield return "";
}
private string ToDuration(TimeSpan value) => value.ToString(FormatString);
}
它可以在以下 .NET 4.8 控制台应用程序中使用:
static void Main(string[] args)
{
if (args.Length != 3 ||
!double.TryParse(args.Last(), NumberStyles.Number, CultureInfo.InvariantCulture,
out var seconds))
{
Console.WriteLine("Use: shiftsrt [inputfile] [outputfile] [seconds]");
Console.WriteLine("Example shiftsrt input.srt output.srt +1.3");
Console.WriteLine("Example shiftsrt input.srt output.srt -4.0");
return;
}
string inputFile = args[0];
string outputFile = args[1];
if (!File.Exists(inputFile))
{
Console.WriteLine($"File not found: {inputFile}.");
return;
}
if (File.Exists(outputFile))
{
Console.WriteLine($"File already exists: {outputFile}.");
return;
}
var subtitles = Subtitle.Parse(File.ReadAllLines(inputFile)).ToArray();
var shiftedSubtitles =
from subtitle in subtitles
select subtitle.Shift(TimeSpan.FromSeconds(seconds));
var shiftedSubtitleLines = (
from subtitle in shiftedSubtitles
from line in subtitle.ToLines()
select line)
.ToArray();
File.WriteAllLines(outputFile, shiftedSubtitleLines);
}