文章目录
  1. 1. SRT文件
  2. 2. 使用C#的正则表达式匹配
  3. 3. 使用C++的正则表达式匹配

SRT文件

.srt是一种非常流行的基于文本行格式的字幕文件。一个简单的字幕文件的内容如下(本文不考虑字幕有多行的情况):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
00:00:53,477 --> 00:00:54,845
大唐

2
00:00:55,212 --> 00:00:57,280
我夙夜忧思的母国

3
00:00:58,615 --> 00:00:59,750
洛阳

4
00:01:00,250 --> 00:01:02,419
我魂萦梦系之地

5
00:01:05,389 --> 00:01:06,890
三十岁那年

其内容包括序号,开始时间和结束时间,以及字幕的文本内容,以\r\n相互隔开,最后以\r\n\r\n结束,这样的格式很容易通过正则表达式匹配来解析。

使用C#的正则表达式匹配

在C#中使用.net的正则表达式匹配,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//data为已经从srt文件中读取的文本
var data = "1\r\n00:00:53,477 --> 00:00:54,845\r\n大唐\r\n\r\n2\r\n00:00:55,212 --> 00:00:57,280\r\n我夙夜忧思的母国\r\n\r\n3\r\n00:00:58,615 --> 00:00:59,750\r\n洛阳\r\n\r\n4\r\n00:01:00,250 --> 00:01:02,419\r\n我魂萦梦系之地\r\n\r\n5\r\n00:01:05,389 --> 00:01:06,890\r\n三十岁那年\r\n\r\n";
var regex = new Regex(@"(\d+)\r\n(\d+:\d{2}:\d{2},\d{3})\s-->\s(\d+:\d{2}:\d{2},\d{3})\r\n(.*)\r\n\r\n");
var matchs = regex.Matches(data);
var lst = new List<Tuple<string, string, string, string>>();
foreach(Match match in matchs)
{
lst.Add(new Tuple<string, string, string, string>(match.Groups[1].Value, match.Groups[2].Value, match.Groups[3].Value, match.Groups[4].Value));
}
foreach (var t in lst)
{
Console.Write("序号: " + t.Item1);
Console.Write(" 开始时间: " + t.Item2);
Console.Write(" 结束时间: " + t.Item3);
Console.WriteLine(" 文本: " + t.Item4);
}

输出如下:

1
2
3
4
5
序号: 1 开始时间: 00:00:53,477 结束时间: 00:00:54,845 文本: 大唐
序号: 2 开始时间: 00:00:55,212 结束时间: 00:00:57,280 文本: 我夙夜忧思的母国
序号: 3 开始时间: 00:00:58,615 结束时间: 00:00:59,750 文本: 洛阳
序号: 4 开始时间: 00:01:00,250 结束时间: 00:01:02,419 文本: 我魂萦梦系之地
序号: 5 开始时间: 00:01:05,389 结束时间: 00:01:06,890 文本: 三十岁那年

使用C++的正则表达式匹配

与.net的正则表达式不同,C++的STL中的正则表达式并没有提供类似Regex.Matchs这种可以匹配多个结果的支持。

  • std::regex_match用于完全匹配一个字符串。
  • std::regex_search用于搜索字符序列中是否存在匹配的一个子串。
  • std::regex_replace用于替换。

但是这并不意味着就没办法用C++的正则表达式来解析了,只是相对.net会稍显麻烦一些。

  • 方法一 使用std::regex_search每次匹配一个结果,下一次从当前匹配的结尾处开始继续往后匹配,如此重复直到匹配失败。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //data为已经从srt文件中读取的文本
    std::string data = "1\r\n00:00:53,477 --> 00:00:54,845\r\n大唐\r\n\r\n2\r\n00:00:55,212 --> 00:00:57,280\r\n我夙夜忧思的母国\r\n\r\n3\r\n00:00:58,615 --> 00:00:59,750\r\n洛阳\r\n\r\n4\r\n00:01:00,250 --> 00:01:02,419\r\n我魂萦梦系之地\r\n\r\n5\r\n00:01:05,389 --> 00:01:06,890\r\n三十岁那年\r\n\r\n";
    //如果编译器不支持C++11的raw string literals 下面一句可以写成 std::regex regex("(\\d+)\\r\\n(\\d+:\\d{2}:\\d{2},\\d{3})\\s-->\\s(\\d+:\\d{2}:\\d{2},\\d{3})\\r\\n(.*)\\r\\n\\r\\n");
    std::regex regex(R"((\d+)\r\n(\d+:\d{2}:\d{2},\d{3})\s-->\s(\d+:\d{2}:\d{2},\d{3})\r\n(.*)\r\n\r\n)");
    std::match_results<std::string::const_iterator> match_results;
    auto begin = data.cbegin();
    std::vector<std::tuple<std::string, std::string, std::string, std::string>> vec;
    while (begin != data.cend())
    {
    if (std::regex_search(begin, data.cend(), match_results, regex))
    {
    vec.emplace_back(match_results[1], match_results[2], match_results[3], match_results[4]);
    begin = match_results.suffix().first;
    }
    else
    {
    break;
    }
    }
    for (const auto& t : vec)
    {
    std::cout << "序号: " << std::get<0>(t)
    << " 开始时间: " << std::get<1>(t)
    << " 结束时间: " << std::get<2>(t)
    << " 文本: " << std::get<3>(t)
    << std::endl;
    }
    输出如下:
    1
    2
    3
    4
    5
    序号: 1 开始时间: 00:00:53,477 结束时间: 00:00:54,845 文本: 大唐
    序号: 2 开始时间: 00:00:55,212 结束时间: 00:00:57,280 文本: 我夙夜忧思的母国
    序号: 3 开始时间: 00:00:58,615 结束时间: 00:00:59,750 文本: 洛阳
    序号: 4 开始时间: 00:01:00,250 结束时间: 00:01:02,419 文本: 我魂萦梦系之地
    序号: 5 开始时间: 00:01:05,389 结束时间: 00:01:06,890 文本: 三十岁那年
  • 方法二 博主暂时没想到比方法一更简便有效的方法。
文章目录
  1. 1. SRT文件
  2. 2. 使用C#的正则表达式匹配
  3. 3. 使用C++的正则表达式匹配