iOS 简单分割小说章节

基本的思路:

  1. 用正则获取章节名称所在的位置(NSRange)
  2. 结合下一章的章节名的位置拿到本章内容信息

具体实现方法

/**
 提取章节的NSRange信息

 @param content 文本内容
 @return `range字符串`数组
 */
+ (NSArray<NSTextCheckingResult *> *)extractChapterListWithContent:(NSString *)content{
    
    NSString* regPattern = @"(\\s)+[第]{0,1}[0-9一二三四五六七八九十百千万]+[章回节卷集幕计][ \t]*(\\S)*";
    NSError* error = NULL;
    NSRegularExpression* regExp = [NSRegularExpression regularExpressionWithPattern:regPattern
                                                                            options:NSRegularExpressionCaseInsensitive
                                                                              error:&error];
    
    return [regExp matchesInString:content options:NSMatchingReportCompletion range:NSMakeRange(0, content.length)];
}

根据 title Range 提取章节所需信息

/**
 根据 title Range 提取章节所需信息
 
 @param content 字符串内容
 @param maintainEmptyCharcter 是否保留空章节
 @return ChapterModel数组
 */
+ (NSArray<ChapterModel *> *)analyseTxtWithContent:(NSString *)content
                             maintainEmptyCharcter:(BOOL)maintainEmptyCharcter{
    
    NSArray<NSTextCheckingResult *> *matchResult = [self extractChapterListWithContent:content];
    NSMutableArray *chapterModels = @[].mutableCopy ;
 
    if (matchResult.count == 0) {
        ChapterModel *model = [ChapterModel new] ;
        model.title = @"内容";
        model.contentRange = NSMakeRange(0, content.length);
        model.allContentRange = NSMakeRange(0, content.length);
        return @[model];
    }
    
    for (NSInteger i = 0; i < matchResult.count ; i++) {
        
        NSRange titleRange = matchResult[i].range;
        NSString *chapterTitle = [[content yj_substringWithRange:titleRange] trimmed];
        NVLog(@"%@",chapterTitle);
        if (i == 0) { //第0章前
            
            NSString *firstTitle = @"开始";
            NSString *contentString = [content yj_substringWithRange:NSMakeRange(0, titleRange.location)];
            if (contentString.trimmed.length > 0 ) {
                
                ChapterModel *model2 = [ChapterModel modelWithTitle:firstTitle
                                                         titleRange:NSMakeRange(0, 0)
                                                    allContentRange:NSMakeRange(0, titleRange.location)];
                [chapterModels addObject:model2];
            }
        }
        
        if (i < matchResult.count-1) {
            
            NSRange nextRange = matchResult[i+1].range;
            if (nextRange.location > titleRange.location) {
                
                NSInteger length = nextRange.location - titleRange.location ;
                ChapterModel *model2 = [ChapterModel modelWithTitle:chapterTitle
                                                         titleRange:titleRange
                                                    allContentRange:NSMakeRange(titleRange.location, length)];
                
                [self chapterModels:chapterModels addModel:model2 content:content maintainEmpty:maintainEmptyCharcter];
            }
        }
        
        if (i == matchResult.count-1){ //最后章节
            
            ChapterModel *model2 = [ChapterModel modelWithTitle:chapterTitle
                                                     titleRange:titleRange
                                                allContentRange:NSMakeRange(titleRange.location,content.length -  titleRange.location)];
            [self chapterModels:chapterModels addModel:model2 content:content maintainEmpty:maintainEmptyCharcter];
        }
    }
    return [chapterModels copy];
}

+ (void)chapterModels:(NSMutableArray *)chapterModels
             addModel:(ChapterModel *)model
              content:(NSString *)content
        maintainEmpty:(BOOL)maintainEmptyCharcter{
    NSInteger contentLength = [[content yj_substringWithRange:model.contentRange] trimmed].length;
    //保留空章节 或者 章节有内容
    if (maintainEmptyCharcter == YES || contentLength > 0) {
        [chapterModels addObject:model];
    }
}
添加辅助方法

新增ChapterModel类:

@interface ChapterModel : NSObject
//章节标题
@property(nonatomic,copy)NSString *title ;
@property(nonatomic)NSRange titleRange ;
//章节内容
@property(nonatomic)NSRange contentRange ;
//内容(包括title)
@property(nonatomic)NSRange allContentRange;
@end 

@implementation ChapterModel

+ (instancetype)modelWithTitle:(NSString *)title
                    titleRange:(NSRange )titleRange
               allContentRange:(NSRange )allContentRange
{
    
    ChapterModel *model = [[ChapterModel alloc]init];
    
    model.title = title ;
    model.titleRange = titleRange ;
    model.allContentRange = allContentRange ;
    model.contentRange = NSMakeRange(titleRange.location + titleRange.length,
                                     allContentRange.length - titleRange.length);

    return model;
}

@end

添加NSString类别,防止substringWithRange越界

@implementation NSString (YJSafe)
//防止越界
- (NSString *)yj_substringWithRange:(NSRange)range{
    
    if (self.length >= range.location + range.length) {
        return [self substringWithRange:range];
    }
    return @"";
}

- (NSString *)trimmed{
    NSCharacterSet* whiteSpaceSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    return [self stringByTrimmingCharactersInSet:whiteSpaceSet];
}

@end
异步获取
/** 
 提取章节信息
 
 @param content 文本内容
 @param isAsync 是否是异步
 @param isNeedMaintainEmptyCharcter 是否需要提取空的章节
 @param result 返回ChapterModel数组
 */
+ (void)extractNovelWithContent:(NSString *)content
                          async:(BOOL)isAsync
          maintainEmptyCharcter:(BOOL)isNeedMaintainEmptyCharcter
                         result:(void(^)(NSArray<ChapterModel *> *models))result {
    
    if (result == nil) {  return ;}
    
    if (isAsync) {
        
        dispatch_async(dispatch_get_global_queue(0,0), ^{
            
            NSArray *models = [self analyseTxtWithContent:content maintainEmptyCharcter:isNeedMaintainEmptyCharcter];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                result(models);
            });
        });
        
    }else {
        result([self analyseTxtWithContent:content maintainEmptyCharcter:isNeedMaintainEmptyCharcter]);
    }
}
预览:

方法调用

2017011818316novel_code.png

效果预览


2017011872898nove_show.png

此外还需要解决的问题:

  1. 这个正则是根据这篇文章修改的,还不能匹配第n章和章节名字多个空格的情况
  2. 有的标题可能比较特别 ,好比【《》目录 第二回 悟彻菩提真妙理 断魔归本合元神】 。 这样匹配“第二回”所在的一行应该比较好
  • 不过还有这样的【《》目录 第十一回 还受生唐王遵善果 度孤魂萧【《》目录 第十二回 玄奘秉诚建大会 观音显象化金蝉 】 ,章节内容直接缺失。 如果匹配所在行估计会出问题。
  • 忽然觉得自己该恶补下正则的知识了。。。。

参考内容
iOS txt小说断章正则表达式实现
TXT小说断章实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,065评论 25 708
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,868评论 18 139
  • 本文笔记源自这里——[实验楼]欢迎大家在下面交流其中有问题的地方喜欢请点收藏,每日更新(全部已亲自实践). 一. ...
    东皇Amrzs阅读 4,037评论 7 54
  • 7月1日开始,进行社群的脑洞大开训练运营。 我当时所具备的条件:①较为丰富的经验②更多的时间③更强的耐心 报名通知...
    博雅大师兄阅读 530评论 0 3
  • 数十日不见楼下阿婆,突然有种不详预感。一日上下楼时隐约间听到聊天对话,几天后才得知,阿婆摔倒已经住院一个多月了,有...
    令令哒阅读 183评论 0 0