有这么个业务需求,抽象出来如下:
- 展示一条文案,比如“某某人做了某某事”,这里的“某某人”是一个用户的昵称;
- 用户可以修改昵称,修改过后再看之前的文案,“某某人”这个昵称需要跟着变化;
- 文案数据从服务端获取;
根据上述需求,讨论确定了服务端下发的“某某人”采用[UserId]uid[/UserId]这样的UBB标签格式,客户端进行解析和替换。
要解析内容,首先要匹配指定的UBB标签,一种简单粗暴的方案就是先查找第一个[UserId],再查找第一个[/UserId],定位到匹配内容。
为了代码好看点,也可以采用正则表达式匹配。第一个版本如下:
NSString *reStr = [NSString stringWithFormat:@"[%@].*[/%@]", ubbTag, ubbTag];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:reStr options:0 error:NULL];
这个版本只能匹配 "UserId]uid[/UserId" ,遗漏了开始和结束的方括号。这时,很久没写正则的我才意识到没有转义。于是有了第二个版本:
NSString *reStr = [NSString stringWithFormat:@"\[%@].*?[/%@\]", ubbTag, ubbTag];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:reStr options:0 error:NULL];
继续失败,经过一番捣鼓,有了第三个版本:
NSString *reStr = [NSString stringWithFormat:@"\\[%@\\].*\\[/%@\\]", ubbTag, ubbTag];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:reStr options:0 error:NULL];
这样就OK了,能匹配到完整的内容。不过当出现多个[UserId]标签时,会贪婪匹配,所以有了第四个版本,进行懒惰匹配:
NSString *reStr = [NSString stringWithFormat:@"\\[%@\\].*?\\[/%@\\]", ubbTag, ubbTag];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:reStr options:0 error:NULL];
当可以准确匹配到UBB标签内容时,就可以对其进行处理了:
#import <Foundation/Foundation.h>
@interface NSMutableString (UBBParser)
- (void)enumUBBTag:(NSString *)ubbTag usingBlock:(void (^)(NSRange tagRange,
NSString *tagContent,
NSMutableString *currentStr,
BOOL *stop))block;
@end
这里基于NSMutableString做了下扩展,参考NSArray的枚举回调接口提供了上述这么一个接口,具体实现可以参考这里的 Gist。
[string enumUBBTag:@"UserId" usingBlock:^(NSRange tagRange, NSString *tagContent, NSMutableString *currentStr, BOOL *stop) {
NSString *currentDisplayName = ...
if (currentDisplayName.length == 0) currentDisplayName = tagContent;
[currentStr replaceCharactersInRange:tagRange withString:currentDisplayName];
}];
上面是处理的调用。