如何使用C#修改文本文件中的特定行?

3
我有一个带有电影字幕的srt文件,格式如下:
1 00:00:00,082 --> 00:00:04,352 bbb bbb bb bbb bb
2 00:00:08,486 --> 00:00:12,662 bbb bbb bbb
3 00:00:12,824 --> 00:00:14,963 bbb bbbb bb
我想给分钟添加一个恒定值(以便稍后显示字幕)。我该怎么做?
我已经有了这段代码:
class MainClass
{

    public static void Main (string[] args)
    {
        StringBuilder sb = new StringBuilder();

        using(FileStream fs = new FileStream(@"sb.srt",FileMode.Open,FileAccess.ReadWrite))
        {
            using(StreamReader sr = new StreamReader(fs))
            {
                while( sr.Read()!=-1 )
                {
                    sb.AppendLine(sr.ReadLine());
                }
            }
        }
    }
}

你知道已经有数百万个程序可以做到这一点吗?大多数播放器也可以实现此功能。 - Oskar Kjellin
问题的标题有误导性。 - jv42
@jv42 然后更改标题,不要投票关闭。 - Oskar Kjellin
我会避免在.NET中使用while(sr.Read()!=-1)来测试文件结尾,因为有更好的方法来检测它。也许在你的情况下,你只想读取整个文件,那么也有一些特殊的方法可以做到这一点,其中一些方法不需要使用流和StreamReader,例如File.ReadAllText(string path) - jv42
@Oskar:关闭投票是因为这个问题太泛泛了,就像“请为我编写这个代码”。 - jv42
显示剩余4条评论
3个回答

4

这是一个完整的程序来实现它。只需将时间周期更改为您想要添加的值即可。

const string format = @"hh\:mm\:ss\,fff";
        static void Main(string[] args)
        {
            string input = File.ReadAllText("sb.srt");
            Regex r = new Regex(@"(\d\d):(\d\d):(\d\d),(\d\d\d)");
            input = r.Replace(input, m=> AddTime(m));
            File.WriteAllText("sb.srt", input);
        }

        private static string AddTime(Match m)
        {
            TimeSpan t = TimeSpan.ParseExact(m.Value, format, CultureInfo.InvariantCulture);
            t += new TimeSpan(0, 1, 0);
            return t.ToString(format);
        }

在AddTime方法第一行,我将TimeSpan替换为DateTime,它几乎可以正常工作。对于输入TimeSpan(0,0,1):00:00:00,082 --> 00:00:04,352,输出为:12:00:01,082 --> 12:00:05,352。12小时?什么鬼?但是1秒已经被正确添加了。 - Arvangen
为什么你在使用DateTime?这是一个时间段。 - Oskar Kjellin
错误 CS0117: System.TimeSpan'不包含ParseExact'的定义(CS0117) (sub) 我遇到了这个错误,但是DateTime类有ParseExact方法。目前我正在使用MonoDevelop(我不知道这是否有区别)。 - Arvangen
好的,现在我在 MSDN 页面上看到 TimeSpan 有这个方法。我认为这可能是 Mono 的问题。 - Arvangen

2
我会使用正则表达式匹配这些行。
00:00:08,486 --> 00:00:12,662

然后使用带有自定义MatchEvaluatorRegex.Replace,该函数解析时间代码、添加时间差并将新的时间代码作为字符串返回。

时间代码的简单正则表达式如下:

"^\s*(\d\d):(\d\d):(\d\d),(\d\d\d)\s*-->\s*(\d\d):(\d\d):(\d\d),(\d\d\d)\s*$"

对于评估器,您可以从比赛的相应字段构建两个TimeSpan值,添加时间差并使用String.Format构建结果字符串。
这样,您可以使用File.ReadAllText将整个文本文件加载到字符串中,使用File.WriteAllText处理它并写回,这使得核心例程成为三行代码 :)
PS:不要忘记使用RegexOptions.Multiline选项以便正确地使用"^""$"

1
    private static string ProcessLine(string line, int seconds)
    {
        var regex = new Regex(@"^(\d\d:\d\d:\d\d,\d\d\d) --> (\d\d:\d\d:\d\d,\d\d\d)");

        var match = regex.Match(line);

        if (match.Success)
        {
            var from = AddSeconds(match.Groups[1].ToString(), seconds);
            var to = AddSeconds(match.Groups[2].ToString(), seconds);
            return string.Format("{0} --> {1}", from, to);
        }
        else
        {
            return line;
        }
    }

    private static string AddSeconds(string timestamp, int seconds)
    {
        var datetime = DateTime.ParseExact(timestamp, "HH:mm:ss,fff", CultureInfo.InvariantCulture);
        return datetime.AddSeconds(seconds).ToString("HH:mm:ss,fff"); 
    }

在你的原始代码中,将 sb.AppendLine(sr.ReadLine()); 替换为 sb.AppendLine(ProcessLine(sr.ReadLine(), 60));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接