• iOS


    最近做了一个WIFI传书本地阅读功能,有所收获在这里记录下吧。

    用户下载的书籍分为两种,一种是有章节格式的,比如 第一章,001章、等,这种可以用正则来直接分章节,还有绝大多数书籍是没有这种格式的,这种如果整本书来直接解析的话,对CPU要求比较大,可能会卡死闪退,所有手动分章节还是很有必要的,这种情况下我们采用按照两千字来分。

    话不多说,开始吧。

    1、WIFI传书把书传到APP沙盒里,这里我们采用的是 GCDWebServer ,很方便,这里就不做陈述了。

    2、将沙盒里面的 .txt 文件转成 文本 ,这里的坑点也不少,我们专门写了一个 NSStringEncoding 解码的算法来转文字,可以解析多种编码方式的文本,这种算法只能适配iOS11及以上系统,其他系统只能采用系统UTF-8方法来解析,限制较多。

    //转成文字
    - (void)encodeWithURL:(NSString *)url result:(void (^)(NSString *content))result;                    
    
    - (void)encodeWithURL:(NSString *)url result:(void (^)(NSString *content))result {
        if (url.length == 0) {
            result(@"");
            return;
        }
        NSData *data = [NSData dataWithContentsOfFile:url options:NSDataReadingMappedIfSafe error:nil];
        
        if (@available(iOS 11.0, *)) {
            NSString *content = data.mc_autoString;
            if (content.length == 0) {
                NSString *txt = data.utf8String;
                txt = [txt stringByReplacingOccurrencesOfString:@"
    " withString:@"
    "];
                txt = [txt stringByReplacingOccurrencesOfString:@"
    " withString:@"
    "];
                result(txt);
                return;
            }
            result(content);
            return;
        }
        
        NSString *txt = data.utf8String;
        txt = [txt stringByReplacingOccurrencesOfString:@"
    " withString:@"
    "];
        txt = [txt stringByReplacingOccurrencesOfString:@"
    " withString:@"
    "];
        result(txt);
    }

    3、文本拿到之后开始分章节吧

     //分章节
    - (void)separateChapterContent:(NSString *)content result:(void (^)(NSArray *chapterArr))result;       
    
    - (void)separateChapterContent:(NSString *)content result:(void (^)(NSArray *chapterArr))result {
        NSMutableArray *chapters = [[NSMutableArray alloc] init];
        NSString *parten = @"第[0-9一二三四五六七八九十百千]*[章回].*";
        NSError* error = NULL;
        NSRegularExpression *reg = [NSRegularExpression regularExpressionWithPattern:parten options:NSRegularExpressionCaseInsensitive error:&error];
        
        NSArray* match = [reg matchesInString:content options:NSMatchingReportCompletion range:NSMakeRange(0, [content length])];
        
        if (match.count >= 100)
        {
            __block NSRange lastRange = NSMakeRange(0, 0);
            [match enumerateObjectsUsingBlock:^(NSTextCheckingResult *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSRange range = [obj range];
                NSInteger local = range.location;
                if (idx == 0) {
                    NSDictionary *dict = @{@"title":@"序章",
                                           @"content":[content substringWithRange:NSMakeRange(0, local)]
                    };
                    [chapters addObject:dict];
                }
                if (idx > 0 ) {
                    NSUInteger len = local-lastRange.location;
                    NSDictionary *dict = @{@"title":[content substringWithRange:lastRange],
                                           @"content":[content substringWithRange:NSMakeRange(lastRange.location, len)]
                    };
                    [chapters addObject:dict];
    
                }
                if (idx == match.count-1) {
                    NSDictionary *dict = @{@"title":[content substringWithRange:range],
                                           @"content":[content substringWithRange:NSMakeRange(local, content.length-local)]
                    };
                    [chapters addObject:dict];
                }
                lastRange = range;
            }];
        } else {
            //不能分章节的书籍按照2000字来手动分章节
            NSArray *lineAry = [content componentsSeparatedByString:@"
    "];
            
            NSMutableArray *bodyTextAry = [[NSMutableArray alloc] init];
            NSInteger outLength = 0;                                        //末尾长度
            NSInteger startLength = 0;                                      //起始长度
            NSInteger textLeng = content.length;                            //总长度
            NSLog(@"总长度:%ld",textLeng);
            
            //先把文字按2000字分出来
            for (int i = 0; i < lineAry.count ; i ++) {
                NSString *textLine = lineAry[i];
                outLength += textLine.length;
                
                if (i+1 != lineAry.count) {
                    ++outLength;
                }
                
                if (outLength >= 2000) {
                    NSRange lastRange = NSMakeRange(startLength, outLength);
                    [bodyTextAry addObject:[content substringWithRange:lastRange]];
                    startLength += outLength;
                    outLength = 0;
                } else if (i == lineAry.count - 1) {
                    NSRange lastRange = NSMakeRange(startLength, outLength);
                    [bodyTextAry addObject:[content substringWithRange:lastRange]];
                }
            }
            //再构造数据传出去
            [bodyTextAry enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSDictionary *dict = @{@"title":[NSString stringWithFormat:@"第%lu章",(unsigned long)idx+1],
                                       @"content":obj
                };
                [chapters addObject:dict];
            }];
            
        }
        
        result(chapters);
    }

    4、章节分好之后就存入本地数据库,然后传入阅读器解析阅读吧。

    注:补充下文本转文字的算法吧

    第一种,如果文本是以utf-8来编码的

    NSData+Addition.h
    
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface NSData (Addition)
    
    #pragma mark - Encode and Decode
    
    /**
     UTF8编码
     */
    - (nullable NSString *)utf8String;
    
    /**
     Base64编码
     */
    - (nullable NSString *)base64EncodedString;
    
    /**
     Base64解码
    
     @param base64EncodedString The encoded string.
     */
    + (nullable NSData *)dataWithBase64EncodedString:(NSString *)base64EncodedString;
    
    /**
     Json解析
     如果失败,返回 nil
     */
    - (nullable id)jsonValueDecodedWithError:(NSError **)error;
    
    #pragma mark - Hash
    
    /**
     MD5编码
     */
    - (NSString *)md5String;
    
    @end
    
    NSData+Addition.m
    
    #import "NSData+Addition.h"
    #include <CommonCrypto/CommonCrypto.h>
    
    @implementation NSData (Addition)
    
    #pragma mark - Encode and Decode
    
    static const char base64EncodingTable[64]
    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    static const short base64DecodingTable[256] = {
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2,  -1,  -1, -2, -2,
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
        -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62,  -2,  -2, -2, 63,
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2,  -2,  -2, -2, -2,
        -2, 0,  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, -2,  -2,  -2, -2, -2,
        -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,  37,  38, 39, 40,
        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2,  -2,  -2, -2, -2,
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2,
        -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,  -2,  -2, -2, -2
    };
    
    - (NSString *)utf8String {
        if (self.length > 0) {
            return [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];
        }
        return @"";
    }
    
    - (NSString *)base64EncodedString {
        NSUInteger length = self.length;
        if (length == 0)
            return @"";
        
        NSUInteger out_length = ((length + 2) / 3) * 4;
        uint8_t *output = malloc(((out_length + 2) / 3) * 4);
        if (output == NULL)
            return nil;
        
        const char *input = self.bytes;
        NSInteger i, value;
        for (i = 0; i < length; i += 3) {
            value = 0;
            for (NSInteger j = i; j < i + 3; j++) {
                value <<= 8;
                if (j < length) {
                    value |= (0xFF & input[j]);
                }
            }
            NSInteger index = (i / 3) * 4;
            output[index + 0] = base64EncodingTable[(value >> 18) & 0x3F];
            output[index + 1] = base64EncodingTable[(value >> 12) & 0x3F];
            output[index + 2] = ((i + 1) < length)
            ? base64EncodingTable[(value >> 6) & 0x3F]
            : '=';
            output[index + 3] = ((i + 2) < length)
            ? base64EncodingTable[(value >> 0) & 0x3F]
            : '=';
        }
        
        NSString *base64 = [[NSString alloc] initWithBytes:output
                                                    length:out_length
                                                  encoding:NSASCIIStringEncoding];
        free(output);
        return base64;
    }
    
    + (NSData *)dataWithBase64EncodedString:(NSString *)base64EncodedString {
        NSInteger length = base64EncodedString.length;
        const char *string = [base64EncodedString cStringUsingEncoding:NSASCIIStringEncoding];
        if (string  == NULL)
            return nil;
        
        while (length > 0 && string[length - 1] == '=')
            length--;
        
        NSInteger outputLength = length * 3 / 4;
        NSMutableData *data = [NSMutableData dataWithLength:outputLength];
        if (data == nil)
            return nil;
        if (length == 0)
            return data;
        
        uint8_t *output = data.mutableBytes;
        NSInteger inputPoint = 0;
        NSInteger outputPoint = 0;
        while (inputPoint < length) {
            char i0 = string[inputPoint++];
            char i1 = string[inputPoint++];
            char i2 = inputPoint < length ? string[inputPoint++] : 'A';
            char i3 = inputPoint < length ? string[inputPoint++] : 'A';
            
            output[outputPoint++] = (base64DecodingTable[i0] << 2)
            | (base64DecodingTable[i1] >> 4);
            if (outputPoint < outputLength) {
                output[outputPoint++] = ((base64DecodingTable[i1] & 0xf) << 4)
                | (base64DecodingTable[i2] >> 2);
            }
            if (outputPoint < outputLength) {
                output[outputPoint++] = ((base64DecodingTable[i2] & 0x3) << 6)
                | base64DecodingTable[i3];
            }
        }
        
        return data;
    }
    
    - (nullable id)jsonValueDecodedWithError:(NSError **)error {
        id value = [NSJSONSerialization JSONObjectWithData:self options:kNilOptions error:error];
        return value;
    }
    
    #pragma mark - Hash
    
    - (NSString *)md5String {
        unsigned char result[CC_MD5_DIGEST_LENGTH];
        CC_MD5(self.bytes, (CC_LONG)self.length, result);
        return [NSString stringWithFormat:
                @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                result[0], result[1], result[2], result[3],
                result[4], result[5], result[6], result[7],
                result[8], result[9], result[10], result[11],
                result[12], result[13], result[14], result[15]
                ];
    }
    
    @end

    第二种算法先不发了,,,,

  • 相关阅读:
    Qt:移动无边框窗体(使用Windows的SendMessage)
    github atom 试用
    ENode框架Conference案例转载
    技术
    NET 领域驱动设计实战系列总结
    mac 配置Python集成开发环境
    User、Role、Permission数据库设计ABP
    Oracle 树操作
    Oracle 用户权限管理方法
    Web Api 2, Oracle and Entity Framework
  • 原文地址:https://www.cnblogs.com/qiyiyifan/p/11777325.html
Copyright © 2020-2023  润新知