首页 > 其他分享 >编译原理分析器大作业之字幕分析器

编译原理分析器大作业之字幕分析器

时间:2023-01-26 15:00:52浏览次数:64  
标签:length int 分析器 编译 字幕 Integer sb new


        写这篇文章的主要目的呢是分享一下编译原理大作业——电影字幕分析器,分享一下我的做法,可能采用的做法不是特别好的用法,希望各位多多指点,觉得文章不错给点小赞赞喔!!!

题目介绍

写一个srt字幕解释器,需要分析单词和语法,分别写出词法和语法,实现字幕的偏移,检验字幕单词语义。

字幕解释器功能与设计问题说明:

  1. 分析字幕的单词和语法,分别写出词法和语法的文法
  2. 在文法的基础上设计词法分析器和语法分析器
  3. 建立内存数据结构,缓冲字幕
  4. 实现字幕平移(例如,将字幕整体推迟2秒)等功能,更新字幕文件

分析

文法分析

我们先来看一条字幕的结构:

0

00:00:01,000 --> 00:00:25,000

English subtitle by : Eduun

1

00:00:36,700 --> 00:00:38,700

The late fourth century A.D. the

Roman Empire began to crumble.

通过上面我们可以看到该文法的大致结构为第一行为序号,第二行内容为时间,第三行为字幕内容,因此,我们可以先简单定义对应的语法和语义为,其中d---数字, 词法分析程序可以接受输入参数,如,d(2)表示数字只能是2位,d(0)表示数字不限位数

d(0)

d(2):d(2):d(2),d(3) --> d(2):d(2):d(2),d(3)

c

nn

d(0)

d(2):d(2):d(2),d(3) --> d(2):d(2):d(2),d(3)

c

代码实现

首先对于字幕我们需要用一个实体来文件中的记录,包括字幕的序号,字幕出现时间、字幕结束时间、字幕内容

public class SRT {
private Integer id;
private Integer beginTime;
private Integer endTime;
private String srtBody;
}

时间偏移的实现用一个TimeMove来实现,具体的思想是当需要偏移时间时,我们在原先的基础上加上需要偏移的时间:

public class TimeMove {

int hour;
int minute;
int second;
int msecond;

public long getMilliSecond() {
return (hour * 3600L + minute * 60L + second) * 1000L + msecond;
}
}

上面是主要的功能设计,然后下面我们需要对字幕文件进行解析,将内容保存进内存中,对于字幕的内容我们直接将内容拼接了起来,防止有多行字幕,对于时间的解析,我单独封装了一个方法来进行专门解析,对于空行我们就用一个@去分隔

public void parseSrt(String srtPath){
FileInputStream inputStream;
try {
inputStream = new FileInputStream(srtPath);
} catch (FileNotFoundException e) {
e.printStackTrace();
return;// 有异常,就没必要继续下去了
}
BufferedReader br = new BufferedReader(new InputStreamReader(
inputStream));
String line;
srtMap = new TreeMap<>();
StringBuilder sb = new StringBuilder();
int key = 0;
try {
while ((line = br.readLine()) != null || sb.length() > 0) {
if (!"".equals(line) && line != null) {
sb.append(line).append("@");
continue;
}
String[] parseStrs = sb.toString().split("@");
// 该if为了适应一开始就有空行以及其他不符格式的空行情况
if (parseStrs.length < 3) {
continue;
}
SRT srt = new SRT();
StringBuilder srtBody = new StringBuilder();
TimeToken timeToken = new TimeToken(srt);
// 可能1句字幕,也可能2句及以上。
srt.setId(Integer.valueOf(parseStrs[0].trim()));
timeToken.parseTime(parseStrs[1]);
// 删除最后一个"\n"
for (int i = 2; i < parseStrs.length; i++) {
srtBody.append(parseStrs[i], 0, parseStrs[i].length() - 1);
}
srt.setSrtBody(new String(srtBody.toString().getBytes(), StandardCharsets.UTF_8));
srtMap.put(key, srt);
key++;
// 清空,否则影响下一个字幕元素的解析
sb.delete(0, sb.length());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* startTime --> endTime
* 解析时间
* @param timeToken 时间的句子
*/
public void parseTime(String timeToken) {
StringBuilder sb = new StringBuilder();
List<String> list = new ArrayList<>();
for (int i = 0; i < timeToken.length(); i++) {
if (isToken(timeToken.charAt(i))) {
if (sb.length() == 0 || sb.length() < 12) {
continue;
}
list.add(sb.toString());
sb.delete(0, sb.length());
} else {
if (timeToken.charAt(i) == ' ' || timeToken.charAt(i) == '\n') {
continue;
}
sb.append(timeToken.charAt(i));
}
}
if (sb.length() >=12) {
list.add(sb.toString());
}
setStartTimeAndEndTime(list);
}

private boolean isToken(char ch) {
return "-->".contains(String.valueOf(ch));
}

private void setStartTimeAndEndTime(List<String> time) {
if (time.size() < 2) {
throw new RuntimeException("字幕文件不合法!!!");
}
String startTime = time.get(0);
String endTime = time.get(1);
int lastIndexOfStartTime = startTime.lastIndexOf(',');
int lastIndexOfEndTime = endTime.lastIndexOf(',');
String[] startArray = startTime.substring(0, lastIndexOfStartTime).split(":");
String[] endArray = endTime.substring(0, lastIndexOfEndTime).split(":");
if (startArray.length < 3 || endArray.length < 3) {
throw new RuntimeException("字幕文件不合法!!!");
}
int beginHour = Integer.parseInt(startArray[0]);
int beginMintue = Integer.parseInt(startArray[1]);
int beginSecond = Integer.parseInt(startArray[2]);
int beginMilli = Integer.parseInt(startTime.substring(lastIndexOfStartTime + 1, startTime.length()));
int beginTime = (beginHour * 3600 + beginMintue * 60 + beginSecond)
* 1000 + beginMilli;
srt.setBeginTime(beginTime);

int endHour = Integer.parseInt(endArray[0]);
int endMintue = Integer.parseInt(endArray[1]);
int endSecond = Integer.parseInt(endArray[2]);
// 校验时间格式
if (!isValid(endMintue, endSecond, beginMintue, beginSecond)) {
throw new RuntimeException("时间格式不合法!!!");
}
int endMilli = Integer.parseInt(endTime.substring(lastIndexOfStartTime + 1, endTime.length()));
int endTimeInt = (endHour * 3600 + endMintue * 60 + endSecond )* 1000 + endMilli;
srt.setEndTime(endTimeInt);
}

播放功能,需要加上字幕偏移:

public void controller() {
// 字幕
TreeMap<Integer, SRT> srtMap = solution.getSrtMap();
Long startMillSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
Set<Integer> set = srtMap.keySet();
int index = 0;
SRT srt;
while (index < set.size()) {
srt = srtMap.get(index);
Long nowMillSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
Integer beginTime = srt.getBeginTime();
Integer endTime = srt.getEndTime();
// 加上时间偏移
long diff = nowMillSecond - startMillSecond + timeMove.getMilliSecond();
if (diff < beginTime) {
continue;
}
String srtBody = srt.getSrtBody();
// 更新字幕
if (diff >= beginTime && diff <= endTime) {
view.setLabel(srtBody);
}
if (diff <= endTime) {
continue;
}
view.setLabel("");
index++;
}
}

运行截图展示

编译原理分析器大作业之字幕分析器_List

 不足之处

        上面是我采用的做法,都是按行读取的,没有按字符去读取解析,读取的解析也做得不是很好,对于一些异常输入没有处理好,还有很多的改进的地方。对于给字幕添加颜色这个功能也没有做出来,鄙人写的比较菜,希望也能给各位带来帮助,如果需要我全部的代码可以在下面评论,也会把代码放到我的资源中(不想花积分下载就私信我就好)。

标签:length,int,分析器,编译,字幕,Integer,sb,new
From: https://blog.51cto.com/u_15559794/6023564

相关文章

  • chromium编译
     vs安装  cmd里 使用命里wInver  查看win版本  对应你电脑win10,11的sdk   ......
  • 2023 项目探秘:从零开始编译Asepirte
    前言Aseprite是收费软件,请大家尊重版权,尊重开发者的创作成果。Aseprite官网Asepirte简介Aseprite是一款用于像素作画的软件。可用于游戏精灵(Sprite)或者像素背景等......
  • Nginx1.10 编译安装
    安装环境系统:Centos6.8软件:Nginx1.10.2依赖软件:Pcre、Zlib、Openssl安装前准备安装编译环境yum-yinstallwgetyum-yinstallgccgcc-c++autoconfautomakemakey......
  • 使用VS2019编译EDK2的方法
    原先自己编译的EDK2的情况,有点旧,本次更新EDK2使用2019的编译器编译EDK2需要的工具链如下,自行下载哈:VS2019:Python3.8:​​https://www.python.org/downloads/release/python-......
  • lazarus 编译为Linux gtk2的应用使用TDateTimePIcker日历在tkDate模式日历下拉菜单不
    网友<安全生产监管>发现lazarus编译为Linux gtk2的应用使用TDateTimePIcker日历在tkDate模式,日历下拉菜单不响应鼠标点击,这个问题在windows和linuxqt下没问题。环境:1、L......
  • RK3568源码编译与交叉编译环境搭建
    本篇进行飞凌OK3568-C开发板的Linux系统开发需要用的软件交叉编译环境的配置。对于软件开发,如果只是使用C/C++代码,则在自己的Ubuntu虚拟机中添加RK3568对应的交叉编译器(gcc......
  • 新手云编译Padavan完整教程
    新手云编译Padavan完整教程   一、编译固件需要的代码https://github.com/chongshengB/Padavan-buildhttps://github.com/hanwckf/rt-n56u直接fork两个代......
  • 利用Github Actions定制编译自己的Padavan固件,小白也可轻松上手,无需安装编译环境
    编译时间大概是20-30分钟左右,不同型号的固件时间不同。源码的登录IP:192.168.2.1用户名/密码:admin/adminwifi密码:1234567890交流群:1020793396教程开始:首先打开 ......
  • Nginx调优总结-第六部分编译优化与简单测试
    第六部分编译优化Nginx可以自行编译,所以里面可以设置多个编译策略.也可以自行修改源码,便于比如进行ip_hash的全IP地址验证.也可以修改nginx的版本号等信息,避免内......
  • 反编译 小程序
    原文来自:抓取微信小程序源码-丁少华-博客园(cnblogs.com)我怕原文删除了,我就找不到了想成为一名微信小程序的开发者,前端思路的学习和安全意识是非常有必要的,故务必......