iOS字符串处理笔记(正则表达式、NSScanner扫描、CoreParse解析器)

转自:


搜索

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

  • 最灵活的方法

1
- (NSRange)rangeOfString:(NSString *)aString options:(NSStringCompareOptions)mask range:(NSRange)searchRange locale:(NSLocale *)locale

格式化字符串

  • 3个方法

1
2
3
-initWithFormat:
-initWithFormat:arguments:
+stringWithFormat:

整数

  • 可以同时工作在32位和64位的

1
2
3
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
  • 转换规则

1
2
3
4
5
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是八进制
  • 设置最小字段宽度和最小数字位数

1
2
3
4
5
6
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

1
2
3
4
5
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"

多行文字

  • 使用 来

1
2
3
4
5
6
7
8
9
10
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.
";
  • 等价写法

1
2
3
4
5
6
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.
";

更简洁的方法

1
NSString * string = @ "The man "  @ "who knows everything "  @ "learns nothing"  @ "." ;

替换字符串

  • NSMutableString的四个方法

1
2
3
4
-deleteCharactersInRange:
-insertString:atIndex:
-replaceCharactersInRange:withString:
-replaceOccurrencesOfString:withString:options:range:

NSString的方法

1
2
3
-stringByReplacingOccurrencesOfString:withString:
-stringByReplacingOccurrencesOfString:withString:options:range:
-stringByReplacingCharactersInRange:withString:

NSMutableString不会创建新字符串,性能会好点

1
2
3
4
5
6
7
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];
}

连接字符串

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

字符串解析

正则表达式

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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这样的字符来定义颜色的方法。

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
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
52
53
54
55
56
57
58
59
60
- (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来解析这个表达式

1
2
myView.left = otherView.right * 2 + 10
viewController.view.centerX + myConstant <= self.view.centerX
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
26
27
28
29
30
31
32
33
34
35
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写法:

1
2
3
4
5
6
7
8
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支持的格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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:@ "
" ];

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

1
2
3
4
5
6
7
- (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。里面有个解析类可以用来解析复杂的布局约束,如下:

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

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

1
2
3
4
5
6
7
8
9
(<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

TableView中显示动态文本

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

1
2
3
4
5
6
7
- (void)layoutSubviews
{
      [ super  layoutSubviews];
      self.textLabel.frame = CGRectInset(self.bounds,
           MyTableViewCellInset,
           MyTableViewCellInset);
}

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

1
2
3
4
5
6
7
8
9
10
11
12
- (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

1
2
3
4
5
6
7
8
9
10
11
12
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:]。接下来创建段落的属性

1
2
3
4
5
NSMutableDictionary *bodyAttributes = [body1stAttributes mutableCopy];
NSMutableParagraphStyle *bodyParagraph =
      [bodyAttributes[NSParagraphStyleAttributeName] mutableCopy];
bodyParagraph.firstLineHeadIndent = fontSize;
bodyAttributes[NSParagraphStyleAttributeName] = bodyParagraph;

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

1
2
3
4
5
6
7
8
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,表格布局示例

1
2
3
4
5
6
7
8
9
10
11
12
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];

显示列表的属性设置如下

1
2
3
4
5
6
7
8
9
10
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;

字符串本地化

原文:http://www.starming.com/index.php?v=index&view=63


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值