Swift

iOS Emoji编解码(OBJ-C/Swift)

2021-11-12  本文已影响0人  左方
  1. 编码EMOJI表情字符串
    OBJ-C:
    扩展NSString
//编码EMOJI表情字符串
- (NSString *)encodeEmojiString {
    NSMutableString *attributeString = [[NSMutableString alloc] initWithString:self];
    NSString *regex_emoji = @"[\\ud83c\\udc00-\\ud83c\\udfff]|[\\ud83d\\udc00-\\ud83d\\udfff]|[\\ud83e\\udd00-\\ud83e\\udfff]|[\\u2600-\\u27ff]";
    
    NSError *error = nil;
    NSRegularExpression *re = [NSRegularExpression
                               regularExpressionWithPattern:regex_emoji
                               options:NSRegularExpressionCaseInsensitive
                               error:&error];
    if (!re) {
        DDLogInfo(@"[NSString toMessageString]: %@", [error localizedDescription]);
        return attributeString;
    }
    
    DDLogInfo(@"stringToUnicode:%@,%@",[NSString stringToUnicode:attributeString],attributeString);

    NSArray *resultArray = [re matchesInString:self options:0 range:NSMakeRange(0, self.length)];
    NSMutableArray *imageArray = [NSMutableArray arrayWithCapacity:resultArray.count];

    //根据匹配范围来用编码后的字符串进行相应的替换
    for(NSTextCheckingResult *match in resultArray) {
        //获取数组元素中得到range
        NSRange range = [match range];
        //获取原字符串中对应的值
        NSString *subStr = [self substringWithRange:range];
        //UTF8编码
        NSString *credentialName = [NSString emojiConvert:subStr];
        NSMutableDictionary *imageDic = [NSMutableDictionary dictionaryWithCapacity:2];
        if (credentialName) {
            [imageDic setObject:credentialName forKey:@"image"];
        }
        [imageDic setObject:[NSValue valueWithRange:range] forKey:@"range"];
        //把字典存入数组中
        [imageArray addObject:imageDic];
    }
    //从后往前替换,否则会引起位置问题
    for (int i = (int)imageArray.count -1; i >= 0; i--) {
        NSRange range = [imageArray[i][@"range"] rangeValue];
        //进行替换
        [attributeString replaceCharactersInRange:range withString:imageArray[i][@"image"]];
    }
    return attributeString;
}
//对emoji转码
- (NSString *)emojiConvert:(NSString *)obj {
    DDLogInfo(@"stringToUnicode:对emoji转码");
    NSString *charactersToEscape = @"?!@#$^&%*+,:;='\"`<>()[]{}/\\| ";
    //invertedSet反转字符集,仅包含当前字符集中不存在的字符
    NSCharacterSet *allowedCharacters = [[NSCharacterSet
                                          characterSetWithCharactersInString:charactersToEscape] invertedSet];
    NSString *encodeStr = [obj stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
    NSString *result = [NSString stringWithFormat:@"<<%@>>", encodeStr];
    return result;
}

Swift:

///编码EMOJI表情字符串
    func encodeEmojiString() -> String {
        let regex_emoji = "[\\ud83c\\udc00-\\ud83c\\udfff]|[\\ud83d\\udc00-\\ud83d\\udfff]|[\\ud83e\\udd00-\\ud83e\\udfff]|[\\u2600-\\u27ff]"
        var regularExpression:NSRegularExpression?
        do {
             try regularExpression = NSRegularExpression(pattern: regex_emoji, options: .caseInsensitive)
        } catch {
            return self
        }
        guard let regularExpression = regularExpression else {
            return self
        }
        
        let resultArray = regularExpression.matches(in: self, options: .reportProgress, range: NSMakeRange(0, self.count))
        
        var imageArray:[Dictionary<String, Any>] = []
        //根据匹配范围来用编码后的字符串进行相应的替换
        for match in resultArray {
            //获取数组元素中得到range
            guard let range = toRange(match.range) else { return self }
            //获取原字符串中对应的值
            let subStr = self[range]
            
            //对emoji转码
            let charactersToEscape = "?!@#$^&%*+,:;='\"`<>()[]{}/\\| "
            //invertedSet反转字符集,仅包含当前字符集中不存在的字符
            let allowedCharacters = CharacterSet.init(charactersIn: charactersToEscape).inverted
            let encodeStr = subStr.addingPercentEncoding(withAllowedCharacters: allowedCharacters)
            let credentialName = "<<\(encodeStr ?? "")>>"
            
            let imageDic = ["image":credentialName, "range":range] as [String : Any]
            imageArray.append(imageDic)
        }
        
        var resultStr = self
        //从后往前替换,否则会引起位置问题
        for dict in imageArray.reversed() {
            resultStr.replaceSubrange(dict["range"] as! Range<String.Index>, with: dict["image"] as! String)
        }
        return resultStr
    }
  1. 解码EMOJI表情字符串
    OBJ-C
    扩展NSString
//解码EMOJI表情字符串
- (NSString *)decodeEmojiString {
    NSMutableString *attributeString = [[NSMutableString alloc] initWithString:self];
    NSString *regex_emoji = @"\\<\\<(.*?)\\>\\>";
    
    NSError *error = nil;
    NSRegularExpression *re = [NSRegularExpression
                               regularExpressionWithPattern:regex_emoji
                               options:NSRegularExpressionCaseInsensitive
                               error:&error];
    if (!re) {
        DDLogInfo(@"[NSString toMessageString]: %@", [error localizedDescription]);
        return attributeString;
    }
    
    NSArray *resultArray = [re matchesInString:self options:0 range:NSMakeRange(0, self.length)];
    NSMutableArray *imageArray = [NSMutableArray arrayWithCapacity:resultArray.count];
    
    //根据匹配范围来用解码后的字符串进行相应的替换
    for(NSTextCheckingResult *match in resultArray) {
        //获取数组元素中得到range
        NSRange range = [match range];
        //获取原字符串中对应的值
        NSString *subStr = [self substringWithRange:range];
        //UTF8解码
        NSString *credentialName = [NSString emojiRecovery:subStr];

        NSMutableDictionary *imageDic = [NSMutableDictionary dictionaryWithCapacity:2];
        if (credentialName) {
            [imageDic setObject:credentialName forKey:@"image"];
        }
        [imageDic setObject:[NSValue valueWithRange:range] forKey:@"range"];
        //把字典存入数组中
        [imageArray addObject:imageDic];
    }
    //从后往前替换,否则会引起位置问题
    for (int i = (int)imageArray.count -1; i >= 0; i--) {
        NSRange range = [imageArray[i][@"range"] rangeValue];
        //进行替换
        [attributeString replaceCharactersInRange:range withString:imageArray[i][@"image"]];
    }
    return attributeString;
}

//对emoji解码
- (NSString *)emojiRecovery:(NSString *)obj {
    DDLogInfo(@"stringToUnicode:对emoji解码");
    //去除首尾指定字符串
    NSCharacterSet *characterSet= [NSCharacterSet characterSetWithCharactersInString:@"<>"];
    NSString *trimEndStr = [obj stringByTrimmingCharactersInSet:characterSet];
    NSString *decodeStr = trimEndStr.stringByRemovingPercentEncoding;
    if (StringNotEmpty(decodeStr)) {
        return decodeStr;
    } else {
        return obj;
    }
}

Swift:
也需要扩展NSString

extension NSString {
    ///解码EMOJI表情字符串
    func decodeEmojiString() -> NSString {
        let regex_emoji = "\\<\\<(.*?)\\>\\>"
        var regularExpression:NSRegularExpression?
        do {
             try regularExpression = NSRegularExpression(pattern: regex_emoji, options: .caseInsensitive)
        } catch {
            return self
        }
        guard let regularExpression = regularExpression else {
            return self
        }
        let resultArray = regularExpression.matches(in: (self as String), options: .reportProgress, range: NSMakeRange(0, self.length))
        var imageArray:[Dictionary<String, Any>] = []
        //根据匹配范围来用解码后的字符串进行相应的替换
        for match in resultArray {
            //获取数组元素中得到range
            let range = match.range
            //获取原字符串中对应的值
            let subStr:NSString = self.substring(with: range) as NSString
            //去除首尾指定字符串
            let characterSet = CharacterSet.init(charactersIn: "<>")
            let trimEndStr = subStr.trimmingCharacters(in: characterSet)
            let decodeStr = trimEndStr.removingPercentEncoding
            
            let imageDic = ["image":decodeStr ?? subStr, "range":range] as [String : Any]
            imageArray.append(imageDic)
        }
        
        let resultStr:NSMutableString = NSMutableString.init(string: self)
        //从后往前替换,否则会引起位置问题
        for dict in imageArray.reversed() {
            resultStr.replaceCharacters(in: dict["range"] as! NSRange, with: dict["image"] as! String)
        }
        return resultStr
    }
}

Swift中用到的扩展:

extension String {
     
    //Range转换为NSRange
    func toNSRange(_ range: Range<String.Index>) -> NSRange {
          guard let from = range.lowerBound.samePosition(in: utf16), let to = range.upperBound.samePosition(in: utf16) else {
              return NSMakeRange(0, 0)
          }
          return NSMakeRange(utf16.distance(from: utf16.startIndex, to: from), utf16.distance(from: from, to: to))
      }
     
    //NSRange转换为Range
    func toRange(_ range: NSRange) -> Range<String.Index>? {
        let indexStart = self.index(self.startIndex, offsetBy: range.location)
        let indexEnd = self.index(indexStart, offsetBy: range.length)
        return indexStart..<indexEnd
    }
}
  1. 总结
    坑:
    某些Emoji例如💁♀️,就是💁+♀,采用变型表单,为那些可以显示颜色和其他内容的显示器提供更多信息。

其中♀前后都有不可见字符,用来表示♀是需要和💁合并的。表示形式为:\u200d\ufe0f
而我们在编码Emoji时,将Emoji用<<>>括起来进行发送。
其中Swift语言编码的String,会将>这个符号和表情带的\u200d\ufe0f结合

>字符在String类型下的不同
上图就可以清楚的看出其中的不同。

转换成Unicode↓

字符 转Unicode
💁♀️ \ud83d\udc81\u200d\u2640\ufe0f
💁 \ud83d\udc81
单个♀ \u2640
用于表示Emoji更多信息的♀ \u200d\u2640\ufe0f
> \u0026\u0067\u0074\u003b
> \u0026\u0067\u0074\u003b\u200d
> \u0026\u0067\u0074\u003b\ufe0f

所以在实际使用时,使用NSString来代替String。

上一篇 下一篇

猜你喜欢

热点阅读