【发布时间】:2015-03-31 09:29:57
【问题描述】:
我需要解析“txf”格式的数据文件。这些文件可能包含超过 1000 个条目。由于格式像 JSON 一样定义明确,所以我想做一个像 JSON 这样的通用解析器,它可以序列化和反序列化 txf 文件。
与 JSON 不同,标记无法识别对象或数组。如果出现具有相同标签的条目,我们需要将其视为一个数组。
-
#标记对象的开始。 -
$标记对象的成员 -
/标记对象的结束
以下是示例“txf”文件
#Employees
$LastUpdated=2015-02-01 14:01:00
#Employee
$Id=1
$Name=Employee 01
#Departments
$LastUpdated=2015-02-01 14:01:00
#Department
$Id=1
$Name=Department Name
/Department
/Departments
/Employee
#Employee
/Employee
/Employees
我能够使用 NSScanner 创建一个通用的TXF Parser。但是随着条目的增加,性能需要更多的调整。
我将获得的基础对象编写为plist,并再次比较了我编写的解析器的性能。我的解析器比 plist 解析器慢 10 倍左右。
虽然plist的文件大小是txf的5倍,而且标记字符也更多,但我觉得还有很大的优化空间。
我们非常感谢这方面的任何帮助。
编辑:包括解析代码
static NSString *const kArray = @"TXFArray";
static NSString *const kBodyText = @"TXFText";
@interface TXFParser ()
/*Temporary variable to hold values of an object*/
@property (nonatomic, strong) NSMutableDictionary *dict;
/*An array to hold the hierarchial data of all nodes encountered while parsing*/
@property (nonatomic, strong) NSMutableArray *stack;
@end
@implementation TXFParser
#pragma mark - Getters
- (NSMutableArray *)stack{
if (!_stack) {
_stack = [NSMutableArray new];
}return _stack;
}
#pragma mark -
- (id)objectFromString:(NSString *)txfString{
[txfString enumerateLinesUsingBlock:^(NSString *string, BOOL *stop) {
if ([string hasPrefix:@"#"]) {
[self didStartParsingTag:[string substringFromIndex:1]];
}else if([string hasPrefix:@"$"]){
[self didFindKeyValuePair:[string substringFromIndex:1]];
}else if([string hasPrefix:@"/"]){
[self didEndParsingTag:[string substringFromIndex:1]];
}else{
//[self didFindBodyValue:string];
}
}]; return self.dict;
}
#pragma mark -
- (void)didStartParsingTag:(NSString *)tag{
[self parserFoundObjectStartForKey:tag];
}
- (void)didFindKeyValuePair:(NSString *)tag{
NSArray *components = [tag componentsSeparatedByString:@"="];
NSString *key = [components firstObject];
NSString *value = [components lastObject];
if (key.length) {
self.dict[key] = value?:@"";
}
}
- (void)didFindBodyValue:(NSString *)bodyString{
if (!bodyString.length) return;
bodyString = [bodyString stringByTrimmingCharactersInSet:[NSCharacterSet illegalCharacterSet]];
if (!bodyString.length) return;
self.dict[kBodyText] = bodyString;
}
- (void)didEndParsingTag:(NSString *)tag{
[self parserFoundObjectEndForKey:tag];
}
#pragma mark -
- (void)parserFoundObjectStartForKey:(NSString *)key{
self.dict = [NSMutableDictionary new];
[self.stack addObject:self.dict];
}
- (void)parserFoundObjectEndForKey:(NSString *)key{
NSDictionary *dict = self.dict;
//Remove the last value of stack
[self.stack removeLastObject];
//Load the previous object as dict
self.dict = [self.stack lastObject];
//The stack has contents, then we need to append objects
if ([self.stack count]) {
[self addObject:dict forKey:key];
}else{
//This is root object,wrap with key and assign output
self.dict = (NSMutableDictionary *)[self wrapObject:dict withKey:key];
}
}
#pragma mark - Add Objects after finding end tag
- (void)addObject:(id)dict forKey:(NSString *)key{
//If there is no value, bailout
if (!dict) return;
//Check if the dict already has a value for key array.
NSMutableArray *array = self.dict[kArray];
//If array key is not found look for another object with same key
if (array) {
//Array found add current object after wrapping with key
NSDictionary *currentDict = [self wrapObject:dict withKey:key];
[array addObject:currentDict];
}else{
id prevObj = self.dict[key];
if (prevObj) {
/*
There is a prev value for the same key. That means we need to wrap that object in a collection.
1. Remove the object from dictionary,
2. Wrap it with its key
3. Add the prev and current value to array
4. Save the array back to dict
*/
[self.dict removeObjectForKey:key];
NSDictionary *prevDict = [self wrapObject:prevObj withKey:key];
NSDictionary *currentDict = [self wrapObject:dict withKey:key];
self.dict[kArray] = [@[prevDict,currentDict] mutableCopy];
}else{
//Simply add object to dict
self.dict[key] = dict;
}
}
}
/*Wraps Object with a key for the serializer to generate txf tag*/
- (NSDictionary *)wrapObject:(id)obj withKey:(NSString *)key{
if (!key ||!obj) {
return @{};
}
return @{key:obj};
}
编辑2:
一个样本 TXF file 有超过 1000 个条目。
【问题讨论】:
-
如果您不向我们展示代码,或者至少提供更多关于您似乎在哪里浪费时间的提示,那么您将很难调整您的解析器...
-
@Romain 我已经分享了整个项目的链接,转发相同的链接github.com/Anupdas/AMTXFParser
-
使用 Instruments 运行您的代码并检查其大部分时间花在哪里。一般来说,使用 Core Foundation C 对象可以帮助你加快速度。例如。在性能敏感的地方尝试使用
CFDictionary和CFStringRef而不是NSDisctionary和NSString。 -
您能分享一个包含更多示例数据的大型 TXF 文件吗?
-
@orkoden 感谢您的建议。从基准测试中,我可以看到 CF 和基础变体之间的访问和创建速度存在显着差异。我现在正在处理它们
标签: ios objective-c parsing markup nsscanner