NSString+XJCore.h
//
// NSString+XJCore.h
// XJCoreFramework
//
// Created by Mlike on 15/5/4.
// Copyright (c) 2015年 Mlike. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSString (LSCore)
#pragma mark - 判斷特殊字符
- (BOOL)empty;
- (BOOL)isInteger;
- (BOOL)isFloat;
- (BOOL)isHasSpecialcharacters;
- (BOOL)isHasNumder;
#pragma mark - 時(shí)間戳轉(zhuǎn)換
- (NSDate *)dateValueWithMillisecondsSince1970;
- (NSDate *)dateValueWithTimeIntervalSince1970;
#pragma 計(jì)算字?jǐn)?shù)(中英混合 都算一個(gè))
- (NSInteger)stringLength;
#pragma mark - 計(jì)算是否含有
- (BOOL)containString:(NSString *)string;
- (BOOL)containsChineseCharacter;
#pragma mark - 計(jì)算字符串尺寸
- (CGSize)heightWithWidth:(CGFloat)width andFont:(CGFloat)font;
- (CGSize)widthWithHeight:(CGFloat)height andFont:(CGFloat)font;
#pragma mark - 正則匹配
- (BOOL)isEmail;
- (BOOL)isUrl;
- (BOOL)isTelephone;
- (BOOL)isValidZipcode;
- (BOOL)isPassword;
- (BOOL)isNumbers;
- (BOOL)isLetter;
- (BOOL)isCapitalLetter;
- (BOOL)isSmallLetter;
- (BOOL)isLetterAndNumbers;
- (BOOL)isChineseAndLetterAndNumberAndBelowLine;
- (BOOL)isChineseAndLetterAndNumberAndBelowLine4to10;
- (BOOL)isChineseAndLetterAndNumberAndBelowLineNotFirstOrLast;
- (BOOL)isBelow7ChineseOrBlow14LetterAndNumberAndBelowLine;
#pragma mark - 加密
// md5
- (NSString*)md5;
// sha
- (NSString *)sha1;
- (NSString *)sha256;
- (NSString *)sha384;
- (NSString *)sha512;
// base64
- (NSString *)base64Encode;
- (NSString *)base64Decode;
// des
- (NSString *)encryptWithKey:(NSString *)key;
- (NSString *)decryptWithKey:(NSString *)key;
#pragma mark - 獲得特殊字符串
+ (NSString*)getTimeAndRandomString;
#pragma mark - json轉(zhuǎn)義
- (NSString *)changeJsonEnter;
#pragma mark - email 轉(zhuǎn)換為 312******@qq.com 形式
- (NSString *)emailChangeToPrivacy;
#pragma mark - Emoji
- (BOOL)isIncludingEmoji;
- (instancetype)removedEmojiString;
@end
NSString+XJCore.m
//
// NSString+XJCore.m
// XJCoreFramework
//
// Created by Mlike on 15/5/4.
// Copyright (c) 2015年 Mlike. All rights reserved.
//
#import "NSString+LSCore.h"
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCrypto.h>
#import "GTMBase64.h"
@implementation NSString (LSCore)
#pragma mark - 正則匹配
/**
* 匹配Email
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isEmail {
NSString *regex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
return [pred evaluateWithObject:self];
}
/**
* 匹配URL
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isUrl {
NSString *regex = @"http(s)?:\\/\\/([\\w-]+\\.)+[\\w-]+(\\/[\\w- .\\/?%&=]*)?";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
return [pred evaluateWithObject:self];
}
/**
* 匹配電話號(hào)碼
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isTelephone {
NSString * MOBILE = @"^1(3[0-9]|47|5[0-35-9]|8[025-9])\\d{8}$";
NSString * CM = @"^((13[4-9])|(147)|(15[0-2,7-9])|(178)|(18[2-4,7-8]))\\d{8}|(1705)\\d{7}$";;
NSString * CU = @"^((13[0-2])|(145)|(15[5-6])|(176)|(18[5,6]))\\d{8}|(1709)\\d{7}$";
NSString * CT = @"^((133)|(153)|(177)|(18[0,1,9]))\\d{8}$";
NSString * PHS = @"^0(10|2[0-5789]|\\d{3})\\d{7,8}$";
NSPredicate *regextestmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", MOBILE];
NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
NSPredicate *regextestphs = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", PHS];
return [regextestmobile evaluateWithObject:self] ||
[regextestphs evaluateWithObject:self] ||
[regextestct evaluateWithObject:self] ||
[regextestcu evaluateWithObject:self] ||
[regextestcm evaluateWithObject:self];
}
- (BOOL)isValidZipcode {
const char *cvalue = [self UTF8String];
long len = strlen(cvalue);
if (len != 6) {
return NO;
}
for (int i = 0; i < len; i++)
{
if (!(cvalue[i] >= '0' && cvalue[i] <= '9'))
{
return NO;
}
}
return YES;
}
/**
* 由英文纺且、字母或數(shù)字組成 6-18位
*
* @return YES 驗(yàn)證成功 NO 驗(yàn)證失敗
*/
- (BOOL)isPassword {
NSString * regex = @"^[A-Za-z0-9_]{6,18}$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
return [pred evaluateWithObject:self];
}
/**
* 匹配數(shù)字
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isNumbers {
NSString *regEx = @"^-?\\d+.?\\d?";
NSPredicate *pred= [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
return [pred evaluateWithObject:self];
}
/**
* 匹配英文字母
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isLetter {
NSString *regEx = @"^[A-Za-z]+$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
return [pred evaluateWithObject:self];
}
/**
* 匹配大寫英文字母
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isCapitalLetter {
NSString *regEx = @"^[A-Z]+$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
return [pred evaluateWithObject:self];
}
/**
* 匹配小寫英文字母
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isSmallLetter {
NSString *regEx = @"^[a-z]+$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
return [pred evaluateWithObject:self];
}
/**
* 匹配小寫英文字母
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isLetterAndNumbers {
NSString *regEx = @"^[A-Za-z0-9]+$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
return [pred evaluateWithObject:self];
}
/**
* 匹配中文,英文字母和數(shù)字及_
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isChineseAndLetterAndNumberAndBelowLine {
NSString *regEx = @"^[\u4e00-\u9fa5_a-zA-Z0-9]+$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
return [pred evaluateWithObject:self];
}
/**
* 匹配中文,英文字母和數(shù)字及_ 并限制字?jǐn)?shù)
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isChineseAndLetterAndNumberAndBelowLine4to10 {
NSString *regEx = @"[\u4e00-\u9fa5_a-zA-Z0-9_]{4,10}";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
return [pred evaluateWithObject:self];
}
/**
* 匹配含有漢字、數(shù)字、字母、下劃線不能以下劃線開頭和結(jié)尾
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isChineseAndLetterAndNumberAndBelowLineNotFirstOrLast {
NSString *regEx = @"^(?!_)(?!.*?_$)[a-zA-Z0-9_\u4e00-\u9fa5]+$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
return [pred evaluateWithObject:self];
}
/**
* 最長不得超過7個(gè)漢字,或14個(gè)字節(jié)(數(shù)字盐固,字母和下劃線)正則表達(dá)式
*
* @return YES 成功 NO 失敗
*/
- (BOOL)isBelow7ChineseOrBlow14LetterAndNumberAndBelowLine {
NSString *regEx = @"^[\u4e00-\u9fa5]{1,7}$|^[\dA-Za-z_]{1,14}$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regEx];
return [pred evaluateWithObject:self];
}
#pragma mark - 加密
/**
* md5加密(32位 常規(guī))
*
* @return 加密后的字符串
*/
- (NSString *)md5 {
const char *cStr = [self UTF8String];
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5( cStr, (CC_LONG)strlen(cStr), digest );
NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH *2];
for(int i =0; i < CC_MD5_DIGEST_LENGTH; i++)
[result appendFormat:@"%02x", digest[i]];
return result;
}
/**
* md5加密(16位)
*
* @return 加密后的字符串
*/
- (NSString *)md5_16 {
// 提取32位MD5散列的中間16位
NSString *md5_32=[self md5];
// 即9~25位
NSString *result = [[md5_32 substringToIndex:24] substringFromIndex:8];
return result;
}
/**
* sha1加密
*
* @return 加密后的字符串
*/
- (NSString *)sha1 {
const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:self.length];
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
NSMutableString* result = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH *2];
for(int i =0; i < CC_SHA1_DIGEST_LENGTH; i++) {
[result appendFormat:@"%02x", digest[i]];
}
return result;
}
/**
* sha256加密
*
* @return 加密后的字符串
*/
- (NSString *)sha256 {
const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:self.length];
uint8_t digest[CC_SHA256_DIGEST_LENGTH];
CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
NSMutableString* result = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH *2];
for(int i =0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[result appendFormat:@"%02x", digest[i]];
}
return result;
}
/**
* sha384加密
*
* @return 加密后的字符串
*/
- (NSString *)sha384 {
const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:self.length];
uint8_t digest[CC_SHA384_DIGEST_LENGTH];
CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
NSMutableString* result = [NSMutableString stringWithCapacity:CC_SHA384_DIGEST_LENGTH *2];
for(int i =0; i < CC_SHA384_DIGEST_LENGTH; i++) {
[result appendFormat:@"%02x", digest[i]];
}
return result;
}
/**
* sha512加密
*
* @return 加密后的字符串
*/
- (NSString*)sha512 {
const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:self.length];
uint8_t digest[CC_SHA512_DIGEST_LENGTH];
CC_SHA512(data.bytes, (CC_LONG)data.length, digest);
NSMutableString* result = [NSMutableString stringWithCapacity:CC_SHA512_DIGEST_LENGTH *2];
for(int i =0; i < CC_SHA512_DIGEST_LENGTH; i++)
[result appendFormat:@"%02x", digest[i]];
return result;
}
/**
* base64加密
*
* @return 加密后的字符串
*/
- (NSString *)base64Encode {
NSString *base64String = [GTMBase64 encodeBase64String:self];
return base64String;
}
/**
* base64解密
*
* @return 解密后的字符串
*/
- (NSString *)base64Decode {
NSString *base64String = [GTMBase64 decodeBase64String:self];
return base64String;
}
/**
* DES加密
*
* @param key 加密需要的key
*
* @return 得到加密后的字符串
*/
- (NSString *)encryptWithKey:(NSString *)key
{
return [self encrypt:self encryptOrDecrypt:kCCEncrypt key:key];
}
/**
* DES解密
*
* @param key 解密需要的key
*
* @return 得到解密后的字符串
*/
- (NSString *)decryptWithKey:(NSString *)key
{
return [self encrypt:self encryptOrDecrypt:kCCDecrypt key:key];
}
/**
* 加密或解密
*
* @param sText 需要加密或解密的字符串
* @param encryptOperation kCCDecrypt 解密 kCCEncrypt 加密
* @param key 加密解密需要的key
*
* @return 返回加密或解密之后得到的字符串
*/
- (NSString *)encrypt:(NSString *)sText encryptOrDecrypt:(CCOperation)encryptOperation key:(NSString *)key
{
const void *vplainText;
size_t plainTextBufferSize;
if (encryptOperation == kCCDecrypt)
{
NSData *decryptData = [GTMBase64 decodeData:[sText dataUsingEncoding:NSUTF8StringEncoding]];
plainTextBufferSize = [decryptData length];
vplainText = [decryptData bytes];
}
else
{
NSData* encryptData = [sText dataUsingEncoding:NSUTF8StringEncoding];
plainTextBufferSize = [encryptData length];
vplainText = (const void *)[encryptData bytes];
}
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
NSString *initVec = @"shuai";
const void *vkey = (const void *) [key UTF8String];
const void *vinitVec = (const void *) [initVec UTF8String];
ccStatus = CCCrypt(encryptOperation,
kCCAlgorithm3DES,
kCCOptionPKCS7Padding,
vkey,
kCCKeySize3DES,
vinitVec,
vplainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
NSString *result = nil;
if (encryptOperation == kCCDecrypt)
{
result = [[NSString alloc] initWithData:[NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes] encoding:NSUTF8StringEncoding] ;
}
else
{
NSData *data = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
result = [GTMBase64 stringByEncodingData:data];
}
return result;
}
#pragma mark - 計(jì)算字符串尺寸
/**
* 計(jì)算字符串高度 (多行)
*
* @param width 字符串的寬度
* @param font 字體大小
*
* @return 字符串的尺寸
*/
- (CGSize)heightWithWidth:(CGFloat)width andFont:(CGFloat)font {
NSDictionary *attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:font]};
CGSize size = [self boundingRectWithSize:CGSizeMake(width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin |NSStringDrawingUsesFontLeading |NSStringDrawingTruncatesLastVisibleLine attributes:attribute context:nil].size;
return size;
}
/**
* 計(jì)算字符串寬度
*
* @param height 字符串的高度
* @param font 字體大小
*
* @return 字符串的尺寸
*/
- (CGSize)widthWithHeight:(CGFloat)height andFont:(CGFloat)font {
NSDictionary *attribute = @{NSFontAttributeName: YC_YAHEI_FONT(font)};
CGSize size = [self boundingRectWithSize:CGSizeMake(MAXFLOAT, height) options:NSStringDrawingUsesLineFragmentOrigin |NSStringDrawingUsesFontLeading |NSStringDrawingTruncatesLastVisibleLine attributes:attribute context:nil].size;
return size;
}
#pragma mark - 檢測(cè)是否含有某個(gè)字符
/**
* 檢測(cè)是否含有某個(gè)字符
*
* @param string 檢測(cè)是否含有的字符
*
* @return YES 含有 NO 不含有
*/
- (BOOL)containString:(NSString *)string {
return ([self rangeOfString:string].location == NSNotFound) ? NO : YES;
}
/**
* 是否含有漢字
*
* @return YES 是 NO 不是
*/
- (BOOL)containsChineseCharacter {
for (int i = 0; i < self.length; i++) {
unichar c = [self characterAtIndex:i];
if (c >=0x4E00 && c <=0x9FFF) {
return YES;
}
}
return NO;
}
#pragma mark - 計(jì)算String的字?jǐn)?shù)(中英混合)
/**
* 計(jì)算string字?jǐn)?shù)
*
* @return 獲得的中英混合字?jǐn)?shù)
*/
- (NSInteger)stringLength {
NSInteger strlength = 0;
NSInteger elength = 0;
for (int i = 0; i < self.length; i++) {
unichar c = [self characterAtIndex:i];
if (c >=0x4E00 && c <=0x9FFF) {
// 漢字
strlength++;
} else {
// 英文
elength++;
}
}
return strlength+elength;
}
#pragma mark - 時(shí)間戳轉(zhuǎn)換
/**
* 毫秒級(jí)時(shí)間戳轉(zhuǎn)日期
*
* @return 日期
*/
- (NSDate *)dateValueWithMillisecondsSince1970 {
return [NSDate dateWithTimeIntervalSince1970:[self doubleValue] / 1000];
}
/**
* 秒級(jí)時(shí)間戳轉(zhuǎn)日期
*
* @return 日期
*/
- (NSDate *)dateValueWithTimeIntervalSince1970 {
return [NSDate dateWithTimeIntervalSince1970:[self doubleValue]];
}
#pragma mark - 判斷特殊字符
/**
* 判斷字符串是否為空
*
* @return YES 是 NO 不是
*/
- (BOOL)empty {
return [self length] > 0 ? NO : YES;
}
/**
* 判斷是否為整形
*
* @return YES 是 NO 不是
*/
- (BOOL)isInteger {
NSScanner* scan = [NSScanner scannerWithString:self];
int val;
return[scan scanInt:&val] && [scan isAtEnd];
}
/**
* 判斷是否為浮點(diǎn)形
*
* @return YES 是 NO 不是
*/
- (BOOL)isFloat {
NSScanner* scan = [NSScanner scannerWithString:self];
float val;
return[scan scanFloat:&val] && [scan isAtEnd];
}
/**
* 判斷是否有特殊字符
*
* @return YES 是 NO 不是
*/
- (BOOL)isHasSpecialcharacters {
NSString * englishNameRule = @"^[(A-Za-z0-9)*(\u4e00-\u9fa5)*(,|\\.|荒给,|。|\\:|;|:|刁卜;|!|志电!|\\*|\\×|\\(|\\)|\\(|\\)|#|#|\\$|&#|\\$|&|\\^|@|&#|\\$|&|\\^|@|@|&|\\¥|\\……)*]+$";
NSPredicate * englishpredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", englishNameRule];
if ([englishpredicate evaluateWithObject:self] == YES) {
return YES;
}else{
return NO;
}
}
/**
* 判斷是否含有數(shù)字
*
* @return YES 是 NO 不是
*/
- (BOOL)isHasNumder {
NSString * englishNameRule = @"[A-Za-z]{2,}|[\u4e00-\u9fa5]{1,}[A-Za-z]+$";
NSString * chineseNameRule =@"^[\u4e00-\u9fa5]{2,}$";
NSPredicate * englishpredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", englishNameRule];
NSPredicate *chinesepredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", chineseNameRule];
if ([englishpredicate evaluateWithObject:self] == YES||[chinesepredicate evaluateWithObject:self] == YES) {
return YES;
}else{
return NO;
}
}
#pragma mark - 獲得特殊字符串
//日期+隨機(jī)數(shù)
/**
* 日期+隨機(jī)數(shù)的字符串(比如為文件命名)
*
* @return 得到的字符串
*/
+ (NSString*)getTimeAndRandomString {
int iRandom=arc4random();
if (iRandom<0) {
iRandom=-iRandom;
}
NSDateFormatter *tFormat=[[NSDateFormatter alloc] init];
[tFormat setDateFormat:@"yyyyMMddHHmmss"];
NSString *tResult=[NSString stringWithFormat:@"%@%d",[tFormat stringFromDate:[NSDate date]],iRandom];
return tResult;
}
#pragma mark - json轉(zhuǎn)義
/**
* 將得到的json的回車替換轉(zhuǎn)義字符
*
* @return 得到替換后的字符串
*/
- (NSString *)changeJsonEnter {
return [self stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
}
#pragma mark - email 轉(zhuǎn)換為 312******@qq.com 形式
- (NSString *)emailChangeToPrivacy {
if (![self isEmail]) {
return @"";
}
NSRange range = [self rangeOfString:@"@"];
NSMutableString *changeStr = [NSMutableString stringWithString:self];
if (range.location > 2) {
NSRange changeRange;
changeRange.location = 3;
changeRange.length = range.location - 3;
NSMutableString *needChanegeToStr = [NSMutableString string];
for (int i = 0; i < changeRange.length ; i ++) {
[needChanegeToStr appendString:@"*"];
}
[changeStr replaceCharactersInRange:changeRange withString:needChanegeToStr];
}
return changeStr;
}
#pragma mark - Emoji相關(guān)
/**
* 判斷是否是Emoji
*
* @return YES 是 NO 不是
*/
- (BOOL)isEmoji {
const unichar high = [self characterAtIndex: 0];
// Surrogate pair (U+1D000-1F77F)
if (0xd800 <= high && high <= 0xdbff) {
const unichar low = [self characterAtIndex: 1];
const int codepoint = ((high - 0xd800) * 0x400) + (low - 0xdc00) + 0x10000;
return (0x1d000 <= codepoint && codepoint <= 0x1f77f);
// Not surrogate pair (U+2100-27BF)
} else {
return (0x2100 <= high && high <= 0x27bf);
}
}
/**
* 判斷字符串時(shí)候含有Emoji
*
* @return YES 是 NO 不是
*/
- (BOOL)isIncludingEmoji {
BOOL __block result = NO;
[self enumerateSubstringsInRange:NSMakeRange(0, [self length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock: ^(NSString* substring, NSRange substringRange, NSRange enclosingRange, BOOL* stop) {
if ([substring isEmoji]) {
*stop = YES;
result = YES;
}
}];
return result;
}
/**
* 移除掉字符串中得Emoji
*
* @return 得到移除后的Emoji
*/
- (instancetype)removedEmojiString {
NSMutableString* __block buffer = [NSMutableString stringWithCapacity:[self length]];
[self enumerateSubstringsInRange:NSMakeRange(0, [self length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock: ^(NSString* substring, NSRange substringRange, NSRange enclosingRange, BOOL* stop) {
[buffer appendString:([substring isEmoji])? @"": substring];
}];
return buffer;
}
@end