You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
413 lines
15 KiB
413 lines
15 KiB
// |
|
// NSString+YYAdd.m |
|
// YYKit <https://github.com/ibireme/YYKit> |
|
// |
|
// Created by ibireme on 13/4/3. |
|
// Copyright (c) 2015 ibireme. |
|
// |
|
// This source code is licensed under the MIT-style license found in the |
|
// LICENSE file in the root directory of this source tree. |
|
// |
|
|
|
#import "NSString+YYAdd.h" |
|
#import "NSData+YYAdd.h" |
|
#import "NSNumber+YYAdd.h" |
|
#import "YYKitMacro.h" |
|
|
|
YYSYNTH_DUMMY_CLASS(NSString_YYAdd) |
|
|
|
|
|
@implementation NSString (YYAdd) |
|
|
|
- (NSString *)md2String { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] md2String]; |
|
} |
|
|
|
- (NSString *)md4String { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] md4String]; |
|
} |
|
|
|
- (NSString *)md5String { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] md5String]; |
|
} |
|
|
|
- (NSString *)sha1String { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] sha1String]; |
|
} |
|
|
|
- (NSString *)sha224String { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] sha224String]; |
|
} |
|
|
|
- (NSString *)sha256String { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] sha256String]; |
|
} |
|
|
|
- (NSString *)sha384String { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] sha384String]; |
|
} |
|
|
|
- (NSString *)sha512String { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] sha512String]; |
|
} |
|
|
|
- (NSString *)crc32String { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] crc32String]; |
|
} |
|
|
|
- (NSString *)hmacMD5StringWithKey:(NSString *)key { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] |
|
hmacMD5StringWithKey:key]; |
|
} |
|
|
|
- (NSString *)hmacSHA1StringWithKey:(NSString *)key { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] |
|
hmacSHA1StringWithKey:key]; |
|
} |
|
|
|
- (NSString *)hmacSHA224StringWithKey:(NSString *)key { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] |
|
hmacSHA224StringWithKey:key]; |
|
} |
|
|
|
- (NSString *)hmacSHA256StringWithKey:(NSString *)key { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] |
|
hmacSHA256StringWithKey:key]; |
|
} |
|
|
|
- (NSString *)hmacSHA384StringWithKey:(NSString *)key { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] |
|
hmacSHA384StringWithKey:key]; |
|
} |
|
|
|
- (NSString *)hmacSHA512StringWithKey:(NSString *)key { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] |
|
hmacSHA512StringWithKey:key]; |
|
} |
|
|
|
- (NSString *)base64EncodedString { |
|
return [[self dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString]; |
|
} |
|
|
|
+ (NSString *)stringWithBase64EncodedString:(NSString *)base64EncodedString { |
|
NSData *data = [NSData dataWithBase64EncodedString:base64EncodedString]; |
|
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; |
|
} |
|
|
|
- (NSString *)stringByURLEncode { |
|
if ([self respondsToSelector:@selector(stringByAddingPercentEncodingWithAllowedCharacters:)]) { |
|
/** |
|
AFNetworking/AFURLRequestSerialization.m |
|
|
|
Returns a percent-escaped string following RFC 3986 for a query string key or value. |
|
RFC 3986 states that the following characters are "reserved" characters. |
|
- General Delimiters: ":", "#", "[", "]", "@", "?", "/" |
|
- Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" |
|
In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow |
|
query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" |
|
should be percent-escaped in the query string. |
|
- parameter string: The string to be percent-escaped. |
|
- returns: The percent-escaped string. |
|
*/ |
|
static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 |
|
static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;="; |
|
|
|
NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; |
|
[allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]]; |
|
static NSUInteger const batchSize = 50; |
|
|
|
NSUInteger index = 0; |
|
NSMutableString *escaped = @"".mutableCopy; |
|
|
|
while (index < self.length) { |
|
NSUInteger length = MIN(self.length - index, batchSize); |
|
NSRange range = NSMakeRange(index, length); |
|
// To avoid breaking up character sequences such as 👴🏻👮🏽 |
|
range = [self rangeOfComposedCharacterSequencesForRange:range]; |
|
NSString *substring = [self substringWithRange:range]; |
|
NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; |
|
[escaped appendString:encoded]; |
|
|
|
index += range.length; |
|
} |
|
return escaped; |
|
} else { |
|
#pragma clang diagnostic push |
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations" |
|
CFStringEncoding cfEncoding = CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding); |
|
NSString *encoded = (__bridge_transfer NSString *) |
|
CFURLCreateStringByAddingPercentEscapes( |
|
kCFAllocatorDefault, |
|
(__bridge CFStringRef)self, |
|
NULL, |
|
CFSTR("!#$&'()*+,/:;=?@[]"), |
|
cfEncoding); |
|
return encoded; |
|
#pragma clang diagnostic pop |
|
} |
|
} |
|
|
|
- (NSString *)stringByURLDecode { |
|
if ([self respondsToSelector:@selector(stringByRemovingPercentEncoding)]) { |
|
return [self stringByRemovingPercentEncoding]; |
|
} else { |
|
#pragma clang diagnostic push |
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations" |
|
CFStringEncoding en = CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding); |
|
NSString *decoded = [self stringByReplacingOccurrencesOfString:@"+" |
|
withString:@" "]; |
|
decoded = (__bridge_transfer NSString *) |
|
CFURLCreateStringByReplacingPercentEscapesUsingEncoding( |
|
NULL, |
|
(__bridge CFStringRef)decoded, |
|
CFSTR(""), |
|
en); |
|
return decoded; |
|
#pragma clang diagnostic pop |
|
} |
|
} |
|
|
|
- (NSString *)stringByEscapingHTML { |
|
NSUInteger len = self.length; |
|
if (!len) return self; |
|
|
|
unichar *buf = malloc(sizeof(unichar) * len); |
|
if (!buf) return self; |
|
[self getCharacters:buf range:NSMakeRange(0, len)]; |
|
|
|
NSMutableString *result = [NSMutableString string]; |
|
for (int i = 0; i < len; i++) { |
|
unichar c = buf[i]; |
|
NSString *esc = nil; |
|
switch (c) { |
|
case 34: esc = @"""; break; |
|
case 38: esc = @"&"; break; |
|
case 39: esc = @"'"; break; |
|
case 60: esc = @"<"; break; |
|
case 62: esc = @">"; break; |
|
default: break; |
|
} |
|
if (esc) { |
|
[result appendString:esc]; |
|
} else { |
|
CFStringAppendCharacters((CFMutableStringRef)result, &c, 1); |
|
} |
|
} |
|
free(buf); |
|
return result; |
|
} |
|
|
|
- (CGSize)sizeForFont:(UIFont *)font size:(CGSize)size mode:(NSLineBreakMode)lineBreakMode { |
|
CGSize result; |
|
if (!font) font = [UIFont systemFontOfSize:12]; |
|
if ([self respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { |
|
NSMutableDictionary *attr = [NSMutableDictionary new]; |
|
attr[NSFontAttributeName] = font; |
|
if (lineBreakMode != NSLineBreakByWordWrapping) { |
|
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; |
|
paragraphStyle.lineBreakMode = lineBreakMode; |
|
attr[NSParagraphStyleAttributeName] = paragraphStyle; |
|
} |
|
CGRect rect = [self boundingRectWithSize:size |
|
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading |
|
attributes:attr context:nil]; |
|
result = rect.size; |
|
} else { |
|
#pragma clang diagnostic push |
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations" |
|
result = [self sizeWithFont:font constrainedToSize:size lineBreakMode:lineBreakMode]; |
|
#pragma clang diagnostic pop |
|
} |
|
return result; |
|
} |
|
|
|
- (CGFloat)widthForFont:(UIFont *)font { |
|
CGSize size = [self sizeForFont:font size:CGSizeMake(HUGE, HUGE) mode:NSLineBreakByWordWrapping]; |
|
return size.width; |
|
} |
|
|
|
- (CGFloat)heightForFont:(UIFont *)font width:(CGFloat)width { |
|
CGSize size = [self sizeForFont:font size:CGSizeMake(width, HUGE) mode:NSLineBreakByWordWrapping]; |
|
return size.height; |
|
} |
|
|
|
- (BOOL)matchesRegex:(NSString *)regex options:(NSRegularExpressionOptions)options { |
|
NSRegularExpression *pattern = [NSRegularExpression regularExpressionWithPattern:regex options:options error:NULL]; |
|
if (!pattern) return NO; |
|
return ([pattern numberOfMatchesInString:self options:0 range:NSMakeRange(0, self.length)] > 0); |
|
} |
|
|
|
- (void)enumerateRegexMatches:(NSString *)regex |
|
options:(NSRegularExpressionOptions)options |
|
usingBlock:(void (^)(NSString *match, NSRange matchRange, BOOL *stop))block { |
|
if (regex.length == 0 || !block) return; |
|
NSRegularExpression *pattern = [NSRegularExpression regularExpressionWithPattern:regex options:options error:nil]; |
|
if (!regex) return; |
|
[pattern enumerateMatchesInString:self options:kNilOptions range:NSMakeRange(0, self.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { |
|
block([self substringWithRange:result.range], result.range, stop); |
|
}]; |
|
} |
|
|
|
- (NSString *)stringByReplacingRegex:(NSString *)regex |
|
options:(NSRegularExpressionOptions)options |
|
withString:(NSString *)replacement; { |
|
NSRegularExpression *pattern = [NSRegularExpression regularExpressionWithPattern:regex options:options error:nil]; |
|
if (!pattern) return self; |
|
return [pattern stringByReplacingMatchesInString:self options:0 range:NSMakeRange(0, [self length]) withTemplate:replacement]; |
|
} |
|
|
|
- (char)charValue { |
|
return self.numberValue.charValue; |
|
} |
|
|
|
- (unsigned char) unsignedCharValue { |
|
return self.numberValue.unsignedCharValue; |
|
} |
|
|
|
- (short) shortValue { |
|
return self.numberValue.shortValue; |
|
} |
|
|
|
- (unsigned short) unsignedShortValue { |
|
return self.numberValue.unsignedShortValue; |
|
} |
|
|
|
- (unsigned int) unsignedIntValue { |
|
return self.numberValue.unsignedIntValue; |
|
} |
|
|
|
- (long) longValue { |
|
return self.numberValue.longValue; |
|
} |
|
|
|
- (unsigned long) unsignedLongValue { |
|
return self.numberValue.unsignedLongValue; |
|
} |
|
|
|
- (unsigned long long) unsignedLongLongValue { |
|
return self.numberValue.unsignedLongLongValue; |
|
} |
|
|
|
- (NSUInteger) unsignedIntegerValue { |
|
return self.numberValue.unsignedIntegerValue; |
|
} |
|
|
|
|
|
+ (NSString *)stringWithUUID { |
|
CFUUIDRef uuid = CFUUIDCreate(NULL); |
|
CFStringRef string = CFUUIDCreateString(NULL, uuid); |
|
CFRelease(uuid); |
|
return (__bridge_transfer NSString *)string; |
|
} |
|
|
|
+ (NSString *)stringWithUTF32Char:(UTF32Char)char32 { |
|
char32 = NSSwapHostIntToLittle(char32); |
|
return [[NSString alloc] initWithBytes:&char32 length:4 encoding:NSUTF32LittleEndianStringEncoding]; |
|
} |
|
|
|
+ (NSString *)stringWithUTF32Chars:(const UTF32Char *)char32 length:(NSUInteger)length { |
|
return [[NSString alloc] initWithBytes:(const void *)char32 |
|
length:length * 4 |
|
encoding:NSUTF32LittleEndianStringEncoding]; |
|
} |
|
|
|
- (void)enumerateUTF32CharInRange:(NSRange)range usingBlock:(void (^)(UTF32Char char32, NSRange range, BOOL *stop))block { |
|
NSString *str = self; |
|
if (range.location != 0 || range.length != self.length) { |
|
str = [self substringWithRange:range]; |
|
} |
|
NSUInteger len = [str lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4; |
|
UTF32Char *char32 = (UTF32Char *)[str cStringUsingEncoding:NSUTF32LittleEndianStringEncoding]; |
|
if (len == 0 || char32 == NULL) return; |
|
|
|
NSUInteger location = 0; |
|
BOOL stop = NO; |
|
NSRange subRange; |
|
UTF32Char oneChar; |
|
|
|
for (NSUInteger i = 0; i < len; i++) { |
|
oneChar = char32[i]; |
|
subRange = NSMakeRange(location, oneChar > 0xFFFF ? 2 : 1); |
|
block(oneChar, subRange, &stop); |
|
if (stop) return; |
|
location += subRange.length; |
|
} |
|
} |
|
|
|
- (NSString *)stringByTrim { |
|
NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet]; |
|
return [self stringByTrimmingCharactersInSet:set]; |
|
} |
|
|
|
- (NSString *)stringByAppendingNameScale:(CGFloat)scale { |
|
if (fabs(scale - 1) <= __FLT_EPSILON__ || self.length == 0 || [self hasSuffix:@"/"]) return self.copy; |
|
return [self stringByAppendingFormat:@"@%@x", @(scale)]; |
|
} |
|
|
|
- (NSString *)stringByAppendingPathScale:(CGFloat)scale { |
|
if (fabs(scale - 1) <= __FLT_EPSILON__ || self.length == 0 || [self hasSuffix:@"/"]) return self.copy; |
|
NSString *ext = self.pathExtension; |
|
NSRange extRange = NSMakeRange(self.length - ext.length, 0); |
|
if (ext.length > 0) extRange.location -= 1; |
|
NSString *scaleStr = [NSString stringWithFormat:@"@%@x", @(scale)]; |
|
return [self stringByReplacingCharactersInRange:extRange withString:scaleStr]; |
|
} |
|
|
|
- (CGFloat)pathScale { |
|
if (self.length == 0 || [self hasSuffix:@"/"]) return 1; |
|
NSString *name = self.stringByDeletingPathExtension; |
|
__block CGFloat scale = 1; |
|
[name enumerateRegexMatches:@"@[0-9]+\\.?[0-9]*x$" options:NSRegularExpressionAnchorsMatchLines usingBlock: ^(NSString *match, NSRange matchRange, BOOL *stop) { |
|
scale = [match substringWithRange:NSMakeRange(1, match.length - 2)].doubleValue; |
|
}]; |
|
return scale; |
|
} |
|
|
|
- (BOOL)isNotBlank { |
|
NSCharacterSet *blank = [NSCharacterSet whitespaceAndNewlineCharacterSet]; |
|
for (NSInteger i = 0; i < self.length; ++i) { |
|
unichar c = [self characterAtIndex:i]; |
|
if (![blank characterIsMember:c]) { |
|
return YES; |
|
} |
|
} |
|
return NO; |
|
} |
|
|
|
- (BOOL)containsString:(NSString *)string { |
|
if (string == nil) return NO; |
|
return [self rangeOfString:string].location != NSNotFound; |
|
} |
|
|
|
- (BOOL)containsCharacterSet:(NSCharacterSet *)set { |
|
if (set == nil) return NO; |
|
return [self rangeOfCharacterFromSet:set].location != NSNotFound; |
|
} |
|
|
|
- (NSNumber *)numberValue { |
|
return [NSNumber numberWithString:self]; |
|
} |
|
|
|
- (NSData *)dataValue { |
|
return [self dataUsingEncoding:NSUTF8StringEncoding]; |
|
} |
|
|
|
- (NSRange)rangeOfAll { |
|
return NSMakeRange(0, self.length); |
|
} |
|
|
|
- (id)jsonValueDecoded { |
|
return [[self dataValue] jsonValueDecoded]; |
|
} |
|
|
|
+ (NSString *)stringNamed:(NSString *)name { |
|
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@""]; |
|
NSString *str = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; |
|
if (!str) { |
|
path = [[NSBundle mainBundle] pathForResource:name ofType:@"txt"]; |
|
str = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; |
|
} |
|
return str; |
|
} |
|
|
|
@end
|
|
|