之前坑了QRC格式歌词转LRC格式,那么现在是合并QRC与LRC。貌似也没什么好说的了,就是些文本处理。直接上代码吧。
#import <string>
#import <iostream>
#import <fstream>
#import <regex.h>
using namespace std;
/*!
* @brief 把微秒时间转为LRC中的时间
*
* @discussion 并未考虑超过99:59.99的时间
*
* @return [dd:dd.dd]
*/
const char * microseconds_to_LRC_time(long long microsecond) {
double seconds = microsecond / 1000.0;
int min = seconds / 60;
double sec = seconds - 60 * min;
return [NSString stringWithFormat:@"%02d:%05.2lf",min,sec].UTF8String;
}
@interface QRC : NSObject
/*!
* @brief 合并QRC与LRC
*
* @param QRCPath QRC文件路径
* @param LRCPath LRC文件路径
* @param error 出错时返回错误码和原因
*/
+ (void)ParserQRCDoc:(NSString *)QRCPath CombineLRC:(NSString *)LRCPath Error:(NSError **)error;
@end
@implementation QRC
+ (void)ParserQRCDoc:(NSString *)QRCPath CombineLRC:(NSString *)LRCPath Error:(NSError **)error {
NSMutableArray * array = [NSMutableArray new];
#pragma mark
#pragma mark - Handle QRC
ifstream QRC_in(QRCPath.UTF8String);
if(!QRC_in) {
*error = [[NSError alloc] initWithDomain:@"com.[data deleted].QRC" code:-1 userInfo:@{@"Error":@"No such QRC file"}];
return;
}
string str;
int cflags = REG_EXTENDED;
regex_t reg;
const char * pattern = "^\[([0-9]+)";
// 匹配以'['开头, 并且后面跟着数字的就行了
// 因为是转为LRC, 故提取出每句开始的时间就行
int z = regcomp(®, pattern, cflags);
if (z != 0) {
char ebuf[128];
regerror(z, ®, ebuf, sizeof(ebuf));
*error = [[NSError alloc] initWithDomain:@"com.[data deleted].QRC" code:-2 userInfo:@{@"Error":[NSString stringWithFormat:@"%s",ebuf]}];
regfree(®);
return;
}
regmatch_t pm[10];
const size_t nmatch = 10;
while(getline(QRC_in, str)) {
z = regexec(®, str.c_str(), nmatch, pm, 0);
if (z == REG_NOMATCH) continue;
else if (z != 0) {
char ebuf[128];
regerror(z, ®, ebuf, sizeof(ebuf));
*error = [[NSError alloc] initWithDomain:@"com.[data deleted].QRC" code:-3 userInfo:@{@"Error":[NSString stringWithFormat:@"%s",ebuf]}];
regfree(®);
return;
} else {
const char * line = str.c_str();
long long microseconds = 0;
for (int x = 1; x < nmatch && pm[x].rm_so != -1; ++ x) {
char *mstr = strndup(line + pm[x].rm_so, pm[x].rm_eo-pm[x].rm_so);
microseconds = atoll(mstr);
free(mstr);
}
BOOL print = YES;
char * lrc = (char *)malloc(sizeof(char) * 512);
int index = 0;
for(int i = 0; i < strlen(line); i++) {
if (line[i] == '[' || line[i] == '(') {
print = NO;
continue;
} else if (line[i] == ']' || line[i] == ')') {
print = YES;
continue;
} else {
if (print) {
lrc[index] = line[i];
index++;
}
}
}
lrc[index] = ' ';
[array addObject:@{@"Time":[[NSString alloc] initWithFormat:@"%lld",microseconds],@"Type":@"QRC",@"LRC":[[NSString alloc] initWithCString:lrc encoding:NSUTF8StringEncoding]}];
free(lrc);
}
}
#pragma mark
#pragma mark - Handle LRC
ifstream LRC_in(LRCPath.UTF8String);
if(!LRC_in) {
*error = [[NSError alloc] initWithDomain:@"com.[data deleted].QRC" code:-4 userInfo:@{@"Error":@"No such LRC file"}];
return;
}
const char * LRC_pattern = "^\[([0-9]+):([0-9]+).([0-9]+)";
z = regcomp(®, LRC_pattern, cflags);
if (z != 0) {
char ebuf[128];
regerror(z, ®, ebuf, sizeof(ebuf));
*error = [[NSError alloc] initWithDomain:@"com.[data deleted].QRC" code:-5 userInfo:@{@"Error":[NSString stringWithFormat:@"%s",ebuf]}];
regfree(®);
return;
}
while(getline(LRC_in, str)) {
z = regexec(®, str.c_str(), nmatch, pm, 0);
if (z == REG_NOMATCH) printf("%sn",str.c_str());
else if (z != 0) {
char ebuf[128];
regerror(z, ®, ebuf, sizeof(ebuf));
*error = [[NSError alloc] initWithDomain:@"com.[data deleted].QRC" code:-6 userInfo:@{@"Error":[NSString stringWithFormat:@"%s",ebuf]}];
regfree(®);
return;
} else {
const char * line = str.c_str();
long long microseconds = 0;
for (int x = 1; x < nmatch && pm[x].rm_so != -1; ++ x){
char *mstr = strndup(line + pm[x].rm_so, pm[x].rm_eo-pm[x].rm_so);
if (x == 1) microseconds += atoll(mstr) * 60 * 1000;
else if (x == 2) microseconds += atoll(mstr) * 1000;
else if (x == 3) microseconds += atoll(mstr) * 10;
free(mstr);
}
BOOL print = YES;
char * lrc = (char *)malloc(sizeof(char) * 512);
int index = 0;
for(int i = 0; i < strlen(line); i++){
if (line[i] == '[' || line[i] == '('){
print = NO;
continue;
} else if (line[i] == ']' || line[i] == ')'){
print = YES;
continue;
} else{
if (print){
lrc[index] = line[i];
index++;
}
}
}
lrc[index] = ' ';
NSString * LRC_Content = [[NSString alloc] initWithCString:lrc encoding:NSUTF8StringEncoding];
if (LRC_Content.length > 0) {
[array addObject:@{@"Time":[[NSString alloc] initWithFormat:@"%lld", microseconds], @"Type" : @"LRC", @"LRC" : LRC_Content}];
}
}
}
#pragma mark
#pragma mark - Sort LRC & QRC
[array sortUsingComparator:^NSComparisonResult(NSDictionary * obj1, NSDictionary * obj2) {
long long t1 = [[obj1 valueForKey:@"Time"] longLongValue];
long long t2 = [[obj2 valueForKey:@"Time"] longLongValue];
if (t1 > t2) return NSOrderedDescending;
else if (t1 == t2) return NSOrderedSame;
else return NSOrderedAscending;
}];
#pragma mark
#pragma mark - Combine
for (int i = 1; i < array.count; i++){
if (abs([[[array objectAtIndex:i - 1] valueForKey:@"Time"] longLongValue] - [[[array objectAtIndex:i] valueForKey:@"Time"] longLongValue]) < 600) {
if ([[[array objectAtIndex:i] valueForKey:@"Type"] isEqualToString:@"QRC"]) {
printf("[%s] %s %sn",microseconds_to_LRC_time([[[array objectAtIndex:i-1] valueForKey:@"Time"] longLongValue]),[(NSString *)[[array objectAtIndex:i-1] valueForKey:@"LRC"] UTF8String],[(NSString *)[[array objectAtIndex:i] valueForKey:@"LRC"] UTF8String]);
} else {
printf("[%s] %s %sn",microseconds_to_LRC_time([[[array objectAtIndex:i] valueForKey:@"Time"] longLongValue]),[(NSString *)[[array objectAtIndex:i] valueForKey:@"LRC"] UTF8String],[(NSString *)[[array objectAtIndex:i-1] valueForKey:@"LRC"] UTF8String]);
}
i++;
} else {
printf("[%s] %sn",microseconds_to_LRC_time([[[array objectAtIndex:i] valueForKey:@"Time"] longLongValue]),[(NSString *)[[array objectAtIndex:i] valueForKey:@"LRC"] UTF8String]);
}
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool
{
int limition = argc;
if (argc % 2 != 0) {
limition -= 1;
}
for (int i = 1; i < limition; i+=2) {
NSError *error;
[QRC ParserQRCDoc:[NSString stringWithUTF8String:argv[i]] CombineLRC:[NSString stringWithUTF8String:argv[i+1]] Error:&error];
}
}
return 0;
}