转载

iOS 字符串处理笔记

iOS字符串处理笔记,包括如何使用正则表达式解析,NSScanner扫描,设置和使用CoreParse解析器来解析处理自定义符号等内容

搜索

在一个字符串中搜索子字符串

  • 最灵活的方法
- (NSRange)rangeOfString:(NSString *)aString options:(NSStringCompareOptions)mask range:(NSRange)searchRange locale:(NSLocale *)locale

格式化字符串

  • 3个方法
-initWithFormat: -initWithFormat:arguments: +stringWithFormat:

整数

  • 可以同时工作在32位和64位的
uint64_t p = 2305843009213693951; NSString *s = [NSString stringWithFormat:@"The ninth Mersenne prime is %llu", (unsigned long long) p]; // "The ninth Mersenne prime is 2305843009213693951"
Modifier d, i o, u, x, X
hh signed char unsigned char
h short unsigned short
(none) int unsigned int
l(ell) long unsigned long
j intmax_t uintmax_t
t ptrdiff_t
z size_t
  • 转换规则
int m = -150004021; uint n = 150004021U; NSString *s = [NSString stringWithFormat:@"d:%d i:%i o:%o u:%u x:%x X:%X", m, m, n, n, n, n]; // "d:-150004021 i:-150004021 o:1074160465 u:150004021 x:8f0e135 X:8F0E135" //o是八进制
  • 设置最小字段宽度和最小数字位数
int m = 42; NSString *s = [NSString stringWithFormat:@"'%4d' '%-4d' '%+4d' '%4.3d' '%04d'", m, m, m, m, m]; // "[ 42] [42 ] [ +42] [ 042] [0042]" m = -42; NSString *s = [NSString stringWithFormat:@"'%4d' '%-4d' '%+4d' '%4.3d' '%04d'", m, m, m, m, m]; // "[ -42] [-42 ] [ -42] [-042] [-042]"
  • %p可打印指针,和%#x不同的是它可以同时在32位和64位执行

浮点数

  • 使用%f和%g
double v[5] = {12345, 12, 0.12, 0.12345678901234, 0.0000012345678901234}; NSString *s = [NSString stringWithFormat:@"%g %g %g %g %g", v[0], v[1], v[2], v[3], v[4]]; // "12345 12 0.12 0.123457 1.23457e-06" NSString *s = [NSString stringWithFormat:@"%f %f %f %f %f", v[0], v[1], v[2], v[3], v[4]]; // "12345.000000 12.000000 0.120000 0.123457 0.000001"

多行文字

  • 使用 来
NSString *limerick = @"A lively young damsel named Menzies " @"Inquired: «Do you know what this thenzies?» " @"Her aunt, with a gasp, " @"Replied: "It's a wasp, " @"And you're holding the end where the stenzies. ";
  • 等价写法
NSString *limerick = @"A lively young damsel named Menzies Inquired: «Do you know what this thenzies?» Her aunt, with a gasp, Replied: "It's a wasp, And you're holding the end where the stenzies. ";
  • 更简洁的方法
NSString * string = @"The man " @"who knows everything " @"learns nothing" @".";

替换字符串

  • NSMutableString的四个方法
-deleteCharactersInRange: -insertString:atIndex: -replaceCharactersInRange:withString: -replaceOccurrencesOfString:withString:options:range: 
  • NSString的方法
-stringByReplacingOccurrencesOfString:withString: -stringByReplacingOccurrencesOfString:withString:options:range: -stringByReplacingCharactersInRange:withString: 
  • NSMutableString不会创建新字符串,性能会好点
NSMutableString *string; // 假设我们已经有了一个名为 string 的字符串 // 现在要去掉它的一个前缀,做法如下: NSString *prefix = @"WeDon’tWantThisPrefix" NSRange r = [string rangeOfString:prefix options:NSAnchoredSearch range:NSMakeRange(0, string.length) locale:nil]; if (r.location != NSNotFound) {      [string deleteCharactersInRange:r]; }

连接字符串

NSArray *names = @["Hildr", @"Heidrun", @"Gerd", @"Guðrún", @"Freya", @"Nanna", @"Siv", @"Skaði", @"Gróa"]; NSString *result = [names componentsJoinedByString:@", "];

字符串解析

正则表达式

NSError *error = nil; NSString *pattern = @"(/w+) = #(/p{Hex_Digit}{6})"; NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error]; NSTextCheckingResult *result = [expression firstMatchInString:string options:0 range:NSMakeRange(0, string.length)]; NSString *key = [string substringWithRange:[result rangeAtIndex:1]]; NSString *value = [string substringWithRange:[result rangeAtIndex:2]];

将字符串分解成数组,使用componentsSeparatedByString:这个方法,或者enumerateSubstringsInRange:options:usingBlock:。如果是按照行来进行分解可以使用option这个参数传NSStringEnumerationByLines

NSString *input = @“ backgroundColor = #ff0000 textColor = #0000ff " NSString *pattern = @"(/w+) = #([/da-f]{6})"; NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:NULL]; NSArray *lines = [input componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; NSMutableDictionary *result = [NSMutableDictionary dictionary]; for (NSString *line in lines) {      NSTextCheckingResult *textCheckingResult = [expression firstMatchInString:line           options:0           range:NSMakeRange(0, line.length)];      NSString* key = [line substringWithRange:[textCheckingResult rangeAtIndex:1]];      NSString* value = [line substringWithRange:[textCheckingResult rangeAtIndex:2]];      result[key] = value; } return result;

扫描

  • NSScanner
NSScanner *scanner = [NSScanner scannerWithString:string]; //默认情况下,扫描器会跳过所有空格符和换行符。但这里我们只希望跳过空格符 scanner.charactersToBeSkipped = [NSCharacterSet whitespaceCharacterSet]; //定义一个十六进制字符集 NSCharacterSet *hexadecimalCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789abcdefABCDEF"]; NSMutableDictionary *result = [NSMutableDictionary dictionary]; while (!scanner.isAtEnd) {  NSString *key = nil;  NSString *value = nil;  NSCharacterSet *letters = [NSCharacterSet letterCharacterSet];  BOOL didScan = [scanner scanCharactersFromSet:letters intoString:&key] &&   [scanner scanString:@"=" intoString:NULL] &&   [scanner scanString:@"#" intoString:NULL] &&   [scanner scanCharactersFromSet:hexadecimalCharacterSet intoString:&value] &&  value.length == 6;  result[key] = value;  [scanner scanCharactersFromSet:[NSCharacterSet newlineCharacterSet]   intoString:NULL]; // 继续扫描下一行 } return result; 

解析器

  • 设计一个能够用(100,0,255)或者#ff0000这样的字符来定义颜色的方法。
- (NSDictionary *)parse:(NSString *)string error:(NSError **)error {  self.scanner = [NSScanner scannerWithString:string];  self.scanner.charactersToBeSkipped = [NSCharacterSet whitespaceCharacterSet];  NSMutableDictionary *result = [NSMutableDictionary dictionary];  NSCharacterSet *letters = [NSCharacterSet letterCharacterSet]  while (!self.scanner.isAtEnd) {   NSString *key = nil;   UIColor *value = nil;   BOOL didScan = [self.scanner scanCharactersFromSet:letters intoString:&key] &&    [self.scanner scanString:@"=" intoString:NULL] &&    [self scanColor:&value];   result[key] = value;   [self.scanner scanCharactersFromSet:[NSCharacterSet newlineCharacterSet]    intoString:NULL]; // 继续扫描下一行  } } - (BOOL)scanColor:(UIColor **)out {  return [self scanHexColorIntoColor:out] || [self scanTupleColorIntoColor:out]; } //扫描设置#ff0000这样的 - (BOOL)scanHexColorIntoColor:(UIColor **)out {  NSCharacterSet *hexadecimalCharacterSet =   [NSCharacterSet characterSetWithCharactersInString:@"0123456789abcdefABCDEF"];  NSString *colorString = NULL;  if ([self.scanner scanString:@"#" intoString:NULL] &&   [self.scanner scanCharactersFromSet:hexadecimalCharacterSet   intoString:&colorString] &&   colorString.length == 6) {   *out = [UIColor colorWithHexString:colorString];   return YES;  }  return NO; } - (BOOL)scanTupleColorIntoColor:(UIColor **)out {  NSInteger red, green, blue = 0;  BOOL didScan = [self.scanner scanString:@"(" intoString:NULL] &&   [self.scanner scanInteger:&red] &&   [self.scanner scanString:@"," intoString:NULL] &&   [self.scanner scanInteger:&green] &&   [self.scanner scanString:@"," intoString:NULL] &&   [self.scanner scanInteger:&blue] &&   [self.scanner scanString:@")" intoString:NULL];  if (didScan) {   *out = [UIColor colorWithRed:(CGFloat)red/255.    green:(CGFloat)green/255.    blue:(CGFloat)blue/255.    alpha:1];   return YES;  } else {   return NO;  } } 

符号化处理

先进星扫描,使用NSScanner来解析这个表达式

myView.left = otherView.right * 2 + 10 viewController.view.centerX + myConstant <= self.view.centerX 
NSScanner *scanner = [NSScanner scannerWithString:contents]; NSMutableArray *tokens = [NSMutableArray array]; while (![scanner isAtEnd]) {  for (NSString *operator in @[@"=", @"+", @"*", @">=", @"<=", @"."]) {   if ([scanner scanString:operator intoString:NULL]) {    [tokens addObject:operator];   }  } } //接下来识别非符号的只包含字母的string NSString *result = nil; if ([scanner scanCharactersFromSet:[NSCharacterSet letterCharacterSet]   intoString:&result]) {  [tokens addObject:result]; } //NSScanner有scanDouble:来扫描double double doubleResult = 0; if ([scanner scanDouble:&doubleResult]) {  [tokens addObject:@(doubleResult)]; } //完成后用将需要解析的表达式放入试试 NSString* example = @"myConstant = 100 "  @" myView.left = otherView.right * 2 + 10 "  @"viewController.view.centerX + myConstant <= self.view.centerX"; NSArray *result = [self.scanner tokenize:example]; NSArray *expected = @[@"myConstant", @"=", @100, @"myView", @".", @"left",  @"=", @"otherView", @".", @"right", @"*", @2, @"+",  @10, @"viewController", @".", @"view", @".",  @"centerX", @"+", @"myConstant", @"<=", @"self",  @".", @"view", @".", @"centerX"]; XCTAssertEqualObjects(result, expected); 

进行语法解析,需要语法分析库描述我们的语言。下面代码就是为那个布局约束语言写的解析语法,用的扩展的巴科斯范式 EBNF 写法:

constraint = expression comparator expression comparator = "=" | ">=" | "<=" expression = keyPath "." attribute addMultiplier addConstant keyPath = identifier | identifier "." keyPath attribute = "left" | "right" | "top" | "bottom" | "leading" | "trailing" | "width" | "height" | "centerX" | "centerY" | "baseline" addMultiplier = "*" atom addConstant = "+" atom atom = number | identifier

还有很多Objective-C的语法解析,更多的可以在CocoaPods上找到: http://cocoapods.org/?q=parse 。比较好的就是CoreParse,地址: https://github.com/beelsebob/CoreParse ,但是需要使用它支持的语法。下面就是CoreParse支持的格式:

NSString* grammarString = [@[  @"Atom ::= num@'Number' | ident@'Identifier';",  @"Constant ::= name@'Identifier' '=' value@<Atom>;",  @"Relation ::= '=' | '>=' | '<=';",  @"Attribute ::= 'left' | 'right' | 'top' | 'bottom' | 'leading' | 'trailing' | 'width' | 'height' | 'centerX' | 'centerY' | 'baseline';",  @"Multiplier ::= '*' num@'Number';",  @"AddConstant ::= '+' num@'Number';",  @"KeypathAndAttribute ::= 'Identifier' '.' <AttributeOrRest>;",  @"AttributeOrRest ::= att@<Attribute> | 'Identifier' '.' <AttributeOrRest>;",  @"Expression ::= <KeypathAndAttribute> <Multiplier>? <AddConstant>?;",  @"LayoutConstraint ::= lhs@<Expression> rel@<Relation> rhs@<Expression>;",  @"Rule ::= <Atom> | <LayoutConstraint>;", ] componentsJoinedByString:@" "]; 

一个规则匹配后解析器就找到同样名称的类

- (id)parser:(CPParser *)parser didProduceSyntaxTree:(CPSyntaxTree *)syntaxTree      NSString *ruleName = syntaxTree.rule.name;      if ([ruleName isEqualToString:@"Attribute"]) {           return self.layoutAttributes[[[syntaxTree childAtIndex:0] keyword]];      } ... }

完整的解析器代码在: https://github.com/objcio/issue-9-string-parsing 。里面有个解析类可以用来解析复杂的布局约束,如下:

viewController.view.centerX + 20 <= self.view.centerX * 0.5

可以得到如下结果,方便转换成NSLayoutConstraint对象

(<Expression: self.keyPath=(viewController, view),  self.attribute=9,  self.multiplier=1,  self.constant=20> -1 <Expression: self.keyPath=(self, view),  self.attribute=9,  self.multiplier=0.5,  self.constant=0>) 

字符串的渲染

UILabel

  • label默认显示一行,如果设置numberOfLines为大于1的话可以显示指定行数,如果设置为0,则多少行都显示
  • attributedText属性可以显示富文本
  • label的font,textColor,textAlignment,shadowColor和shadowOffset属性可以改变外观。
  • 改变程序内所有Label的风格,可以使用[UILabel appearance]方法

UITextField

  • text field只限于单行
  • UITextfield实现了UITextInputTraits协议,这个协议需要指定键盘外观和操作等细节。比如显示什么键盘和返回按键响应等
  • 可以通过设置左右辅助视图,或者设置背景来自定义输入框风格了。

UITextView

  • 相比较UITextField,它能够处理多行文本
  • 可以使用定制Text Kit,官方文档: https://developer.apple.com/Library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html
  • 可以为layout manager,text container或text storage自定义行为或替换自定义子类。
  • 在iOS7之前是基于WebKit的功能少,之后会有很多不同具体可以参考这两篇文章:Peter的 http://petersteinberger.com/blog/2014/fixing-uitextview-on-ios-7/ ,Brent的 http://inessential.com/2014/01/07/uitextview_the_solution

TableView中显示动态文本

Table view的Delegate有个方法用来计算高度:tableView:heightForRowAtIndexPath:。自定义一个UITableViewCell的子类

- (void)layoutSubviews {      [super layoutSubviews];      self.textLabel.frame = CGRectInset(self.bounds,           MyTableViewCellInset,           MyTableViewCellInset); }

计算真实高度需要使用boundingRectWithSize:options:context: 这个方法

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {  CGFloat labelWidth = self.tableView.bounds.size.width - MyTableViewCellInset*2;  NSAttributedString *text = [self attributedBodyTextAtIndexPath:indexPath];  NSStringDrawingOptions options = NSStringDrawingUsesLineFragmentOrigin |   NSStringDrawingUsesFontLeading;  CGRect boundingRect = [text boundingRectWithSize:CGSizeMake(labelWidth, CGFLOAT_MAX)   options:options   context:nil];  return (CGFloat) (ceil(boundingRect.size.height) + MyTableViewCellInset*2); } 

使用Text Kit和NSAttributedString进行布局

先设置attributes

CGFloat const fontSize = 15;  NSMutableDictionary *body1stAttributes = [NSMutableDictionary dictionary]; body1stAttributes[NSFontAttributeName] = [UIFont fontWithName:@"BodoniSvtyTwoITCTT-Book" size:fontSize]; NSMutableParagraphStyle *body1stParagraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; body1stParagraph.alignment = NSTextAlignmentJustified; body1stParagraph.minimumLineHeight = fontSize + 3; body1stParagraph.maximumLineHeight = body1stParagraph.minimumLineHeight; body1stParagraph.hyphenationFactor = 0.97; body1stAttributes[NSParagraphStyleAttributeName] = body1stParag raph;

这里字体为BodoniSvtyTwoITCTT,如果需要查看更多字体可以使用 +[UIFont familyNames]这个方法。为了得到字体的名字,可以使用 +[UIFont fontNamesForFamilyName:]。接下来创建段落的属性

NSMutableDictionary *bodyAttributes = [body1stAttributes mutableCopy]; NSMutableParagraphStyle *bodyParagraph =      [bodyAttributes[NSParagraphStyleAttributeName] mutableCopy]; bodyParagraph.firstLineHeadIndent = fontSize; bodyAttributes[NSParagraphStyleAttributeName] = bodyParagraph;

装饰段落风格,使用装饰字体将文本居中对齐,装饰字符的前后加上空白段落

NSMutableDictionary *ornamentAttributes = [NSMutableDictionary dictionary]; ornamentAttributes[NSFontAttributeName] = [UIFont fontWithName:@"BodoniOrnamentsITCTT"  size:36]; NSMutableParagraphStyle *ornamentParagraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; ornamentParagraph.alignment = NSTextAlignmentCenter; ornamentParagraph.paragraphSpacingBefore = fontSize; ornamentParagraph.paragraphSpacing = fontSize; ornamentAttributes[NSParagraphStyleAttributeName] = ornamentParagraph; 

显示数字表格table,表格布局示例

NSCharacterSet *decimalTerminator = [NSCharacterSet  characterSetWithCharactersInString:decimalFormatter.decimalSeparator]; NSTextTab *decimalTab = [[NSTextTab alloc]  initWithTextAlignment:NSTextAlignmentCenter  location:100  options:@{NSTabColumnTerminatorsAttributeName:decimalTerminator}]; NSTextTab *percentTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight  location:200  options:nil]; NSMutableParagraphStyle *tableParagraphStyle =  [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; tableParagraphStyle.tabStops = @[decimalTab, percentTab]; 

显示列表的属性设置如下

NSMutableDictionary *listAttributes = [bodyAttributes mutableCopy]; NSMutableParagraphStyle *listParagraph =  [listAttributes[NSParagraphStyleAttributeName] mutableCopy]; listParagraph.headIndent = fontSize * 3; listParagraph.firstLineHeadIndent = fontSize; NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentNatural  location:fontSize * 3  options:nil]; listParagraph.tabStops = @[listTab]; listAttributes[NSParagraphStyleAttributeName] = listParagraph; 
正文到此结束
Loading...