歌词.lrc文件实际存放的就是二进制的字符串
歌词.lrc文件的解析实际就是二进制字符串解析
内部格式大致为:
[03:02.00][01:05.00]这晚夜没有吻别
主要由两部分组成: 时间部分 和 歌词部分
两个时间戳代表同一句歌词会在两个时间下显示
歌词解析:
1.先抽取一个歌词模型类(存放每一句歌词)
2.声明属性:时间(每句的起始时间) 和 歌词(每一句)
3.创建一个工具类,专门用作解析歌词,供外界直接调用
3.1 读取歌词字符串
3.2 分隔字符串为一个存放每一句歌词的数组(包含时间和歌词)
3.3 遍历存放歌词的数组,根据时间格式使用正则表达式过滤字符串,得到一个存放NSTextCheckingResult对象的数组
3.4 截取时间和歌词,遍历存放歌词(含时间和歌词)的数组:
3.4.1 遍历存放NSTextCheckingResult对象的数组,通过range属性获得时间(因为每句歌词可能含多个时间戳)
3.4.2 通过range属性,截取歌词
3.5 给歌词模型属性赋值
3.6 将得到的歌词模型对象添加到可变数组
3.7 将存放歌词模型对象的数组重新排序
- 时间截取:
1. 每句歌词可能含多个时间戳,所以在遍历歌词数组时,使用正则表达式过滤歌词,会得到一个存放NSTextCheckingResult对象的数组,这里需要使用到NSTextCheckingResult的range属性,再次遍历存放NSTextCheckingResult对象的数组,通过range属性截取时间字符串
2. 截取的是时间字符串,为了便于使用,这里将字符串转为NSTimeInterval格式再进行存储(歌词模型中也是直接使用NSTimeInterval声明的)
- 歌词截取:
参考_歌词内部格式示例_,一句歌词可能会有多个时间戳,根据正则表达式获取到存放时间的数组,取出数组中最后一个元素,也就是每句歌词中最后的一个时间戳:
用最后的时间戳的local + length才是全部时间部分的长度,也就得到了后面歌词的索引
- 示例代码:
歌词模型类(只是声明了属性,并未提供方法):
#import <Foundation/Foundation.h>
@interface JSLyricModel : NSObject
// 该句歌词的初始时间
@property (nonatomic,assign) NSTimeInterval initialTime;
// 该句歌词
@property (nonatomic,copy) NSString *content;
@end
歌词解析工具类:
#import "JSLyricManager.h"
#import "NSDateFormatter+Shared.h"
#import "JSLyricModel.h"
@implementation JSLyricManager
+ (NSArray<JSLyricModel *> *)parserLyricWithFileName:(NSString *)fileName{
// 取出歌词字符串
NSString *filePath = [[NSBundle mainBundle]pathForResource:fileName ofType:nil];
NSString *lyricStr = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
// 分隔字符串
NSArray *lyricArr = [lyricStr componentsSeparatedByString:@"\n"];
/* 正则表达式过滤字符串:
[00:19.00]曲:河合奈保子 词:向雪怀
[02:19.00][00:23.00]仍然倚在失眠夜望天边星宿
*/
NSString *regularExpressionString = @"\\[[0-9]{2}:[0-9]{2}.[0-9]{2}\\]";
NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:regularExpressionString options:0 error:nil];
// 存放歌词对象的临时可变数组
NSMutableArray *tempMarr = [NSMutableArray array];
//遍历歌词数组,取出每一句歌词
for (NSString *element in lyricArr) {
// 正则表达式遍历
NSArray<NSTextCheckingResult *> *textCheckingResult = [regularExpression matchesInString:element options:0 range:NSMakeRange(0, element.length)];
// 2.截取歌词内容
NSTextCheckingResult *lastTimeString = textCheckingResult.lastObject;// 取出得到数组中的最后一个时间元素,用来获取range
// 截取歌词 (一句歌词可能会有多个时间戳,取到最后一个时间戳,用自己的local + length才是全部时间部分的长度,也就得到了后面歌词的索引)
NSString *lyricContent = [element substringFromIndex:lastTimeString.range.length + lastTimeString.range.location];
// 同一句歌词可能多处显示,所以返回值是一个数组,遍历取出每一个起始时间字符串
for (NSTextCheckingResult *result in textCheckingResult) {
// 1. 截取时间字符串
NSString *lyricSubString = [element substringWithRange:NSMakeRange(result.range.location, result.range.length)];
// 截取玩后设置每一句歌词的起始时间
NSTimeInterval currentLyricInitialTime = [self timeIntervalWithTimeString:lyricSubString];
// 创建歌词模型
JSLyricModel *model = [[JSLyricModel alloc]init];
// 模型赋值
model.initialTime = currentLyricInitialTime;// 给歌词模型的歌词初试时间赋值
model.content = lyricContent; // 给歌词模型的歌词内容赋值
// 添加到临时可变数组
[tempMarr addObject:model];
}
}
// 歌词起始时间排序
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"initialTime" ascending:YES];
NSArray *lyricModelArr = [tempMarr sortedArrayUsingDescriptors:@[descriptor]];
// 进行解析
return lyricModelArr;
}
// 将时间字符串转换为NSTimerInterval类型,方便外面直接判断
+ (NSTimeInterval)timeIntervalWithTimeString:(NSString *)timeString{
// 时间字符串 --> 日期对象 NSDate --> 计算对应的时间间隔
NSDateFormatter *dateFormatter = [NSDateFormatter sharedManager];
// 设置格式
dateFormatter.dateFormat = @"[mm:ss.SS]";
// 创建初始时间对象 用来计算时间间隔
NSDate *initialDate = [dateFormatter dateFromString:@"[00:00.00]"];
// 将字符串转换为NSDate
NSDate *targetDate = [dateFormatter dateFromString:timeString];
// 计算时间间隔
return [targetDate timeIntervalSinceDate:initialDate];
}
@end