美文网首页iOS_小蟹专题iOS基础·OC语法篇iOS菜鸟食谱
iOS字符串 如何使用正则表达式解析,NSScanner扫描,设

iOS字符串 如何使用正则表达式解析,NSScanner扫描,设

作者: 星光社的戴铭 | 来源:发表于2015-09-06 17:12 被阅读527次

    搜索

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

    • 最灵活的方法
    - (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"
    

    多行文字

    • 使用\n来
    NSString *limerick = @"A lively young damsel named Menzies\n"
    @"Inquired: «Do you know what this thenzies?»\n"
    @"Her aunt, with a gasp,\n"
    @"Replied: "It's a wasp,\n"
    @"And you're holding the end where the stenzies.\n";
    
    • 等价写法
    NSString *limerick = @"A lively young damsel named Menzies\nInquired: «Do you know what this thenzies?»\nHer aunt, with a gasp,\nReplied: "It's a wasp,\nAnd you're holding the end where the stenzies.\n";
    
    • 更简洁的方法
    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\n"
         @"\nmyView.left = otherView.right * 2 + 10\n"
         @"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:@"\n"];
    

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

    - (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

    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;
    

    字符串本地化

    相关文章

      网友评论

        本文标题:iOS字符串 如何使用正则表达式解析,NSScanner扫描,设

        本文链接:https://www.haomeiwen.com/subject/luorcttx.html