NSScanner的应用实例
NSScanner是系统提供的一个可以扫描字符串中内容的方法,由于location这个属性的特性,NSScanner常常配合循环使用
先看看NSScanner的几个基本属性
@property (readonly, copy) NSString *string; //通过这个属性可以用getter方法导出scanner初始化时设置string
@property NSUInteger scanLocation; //最重要的属性,代表scanner目前扫描的位置,可以使用setter方法
@property (nullable, copy) NSCharacterSet *charactersToBeSkipped; //扫描时需要跳过的字符
@property BOOL caseSensitive; //忽略大小写区别
NSScanner的常用方法
1.scanString: intoString:
在scanner中搜索目标字符串,如果第一个字符和目标字符串相等,return 1,如果第一个字符和目标字符串不相等,return 0.intoString为返回的目标字符串(也可以设置为nil),scanner.scanLocation值变成目标字符串后面一位
2.scanUpToString: intoString:
在scanner中搜索目标字符串,如果第一个字符和目标字符串相等,return 0,intoString为返回的目标字符串(也可以设置为nil),scanner.scanLocation值保留目标字符串前面一位
Scanner的实际应用
#import "NSString+Scanner.h" //创建了一个NSString的Category,方便方法的调用
@implementation NSString (Scanner)
//1.查找某个string里是否含有int值
- (BOOL)isPureInt
{
NSScanner *scanner = [[NSScanner alloc] initWithString:self];
int intValue;
return [scanner scanInt:&intValue] && [scanner isAtEnd];
}
//2.查找给定String中所含有的全部浮点数值
- (void)scanFloatValue
{
NSScanner *scanner = [[NSScanner alloc] initWithString:self];
float floatValue;
while (![scanner isAtEnd])
{
if ([scanner scanFloat:&floatValue])
{
NSLog(@"floatValue = %.2f",floatValue);
}
scanner.scanLocation++;//由于scanLocation的特殊机制,这里需要注意的是如果最后字符串最后一位是浮点数类型的话会crash,在最后一位拼接上一个字母就可以正常运行了.
}
}
在main. m中可以用NSString的对象直接调用方法
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "NSString+Scanner.h"
int main(int argc, char * argv[]) {
NSString *intString = @"211231233423523511";
if ([intString isPureInt])
{
NSLog(@"是纯int");
}
else
{
NSLog(@"不是纯int");
}
//输出:是纯int
NSString *floatString = @"1.5,2.8,-0.99,pre hour;";
[floatString scanFloatValue];
//输出:floatValue = 1.50
// floatValue = 2.80
// floatValue = -0.99
}
3.假设有个TXT格式的文本:
<abc1>fwerwer
<abc2>asfdapeirjqpierj;;
wwr<abc3>
<aa>
想要从中提取出<>内的内容
代码如下:
NSString *tmp;
NSArray *lines;
lines = [[NSString stringWithContentsOfFile:@"/Users/linjiaqiao/Documents/Now/Lesson-OC-04/Lesson-OC-04/安装包密码.txt" encoding:NSUTF8StringEncoding error:nil] componentsSeparatedByString:@"\n"];
NSLog(@"lines = %@",lines);
NSEnumerator *nse = [lines objectEnumerator];//创建一个枚举器
// 读取<>里的内容
while(tmp = [nse nextObject])
{
NSString *test1 = nil;
NSString *test2 = nil;
NSString *stringBetweenBrackets = nil;
NSScanner *scanner = [NSScanner scannerWithString:tmp];
[scanner scanUpToString:@"<" intoString:&test1]; // "<"之前的字符串装入test1中,scanLocation在"<"前的位置 如果第一位就是"<"的话返回值为0
[scanner scanString:@"<" intoString:&test2]; // "<"装入test2中,scanLocation在"<"后的位置 如果第一位是"<"的话返回值为1
[scanner scanUpToString:@">" intoString:&stringBetweenBrackets];
NSLog(@"<>内的内容 = %@",[stringBetweenBrackets description]);
NSLog(@"test1 = %@",test1);
NSLog(@"test2 = %@",test2);
//输出结果:
2016-02-16 17:12:40.667 Lesson-OC-04[6530:503881] <>内的内容 = abc1
2016-02-16 17:12:40.667 Lesson-OC-04[6530:503881] test1 = (null)
2016-02-16 17:12:40.668 Lesson-OC-04[6530:503881] test2 = <
2016-02-16 17:12:40.668 Lesson-OC-04[6530:503881] <>内的内容 = abc2
2016-02-16 17:12:40.668 Lesson-OC-04[6530:503881] test1 = (null)
2016-02-16 17:12:40.669 Lesson-OC-04[6530:503881] test2 = <
2016-02-16 17:12:40.669 Lesson-OC-04[6530:503881] <>内的内容 = abc3
2016-02-16 17:12:40.669 Lesson-OC-04[6530:503881] test1 = wwr
2016-02-16 17:12:40.670 Lesson-OC-04[6530:503881] test2 = <
2016-02-16 17:12:40.670 Lesson-OC-04[6530:503881] <>内的内容 = aa
2016-02-16 17:12:40.670 Lesson-OC-04[6530:503881] test1 = (null)
2016-02-16 17:12:40.670 Lesson-OC-04[6530:503881] test2 = <
可以看出每次循环先要用scanUpToString: intoString:方法找出@"<",此时location在@"<"前,再用scanString: intoString:方法使location在@"<"后,最后再次使用scanUpToString: intoString:方法把从location位置到@">"前的字符串都提取出来.
NSScanner的扫描操作从上次扫描的位置开始,并且继续往后扫描直到指定的内容出现为止(如果有的话)。以字符串“137 small cases of bananas”为例,在扫描完一个整数之后,scanner的位置将变为3,也即数字后面的空格处。通常,你会继续扫描并跳过你不关心的字符。那么你可以用setScanLocation:方法跳过某几个字符(也可以用这个方法在发生某些错误后,重新开始扫描字符串的某部分)。如果你想跳过某种特殊的字符集中的字符时,可以使用setCharactersToBeSkipped:方法。scanner在任何扫描操作时会跳过空白字符之后才开始。但是当它找到一个可以扫描的字符时,它会用全部字符去和指定内容匹配。scanner默认情况下会忽略空白字符和换行符。注意,对于忽略字符,总是大小写敏感的。例如要忽略所有原音字母,你必须使用“AEIOUaeiou”,而不能仅仅是“AEIOU”或“aeiou”。 如果你想获取当前位置的某个字符串的内容,可以使用scanUpToString:intoString:方法(如果你不想保留这些字符,可以传递一个NULL给第2个参数)。
例1 以下列字符串为例:
137 small cases of bananas
下面的代码,可以从字符串中找出包装规格(small cases)和包装数量(137)。
NSString *bananas = @"137 small cases of bananas";
NSString *separatorString = @" of";
NSScanner *aScanner = [NSScanner scannerWithString:bananas];
NSInteger anInteger;
[aScanner scanInteger:&anInteger];
NSLog(@"%ld",anInteger); //137
NSString *container;
NSLog(@"此时scanLocatoin = %ld",aScanner.scanLocation); //此时scanLocatoin = 3
[aScanner scanUpToString:separatorString intoString:&container];//upToString 代表扫描碰到什么字符串结束,location在此字符串前
NSLog(@"container = %@",container);
NSLog(@"此时scanLocatoin = %ld",aScanner.scanLocation); //此时scanLocatoin = 15
查找字符串separatorString为“ of”关系重大。默认scanner会忽略空白字符,因此在数字137后面的空格被忽略。但是当scanner从空格后面的字符开始时,所有的字符都被加到了输出字符串中,一直到遇到搜索字符串(“of”)。
如果搜索字符串是“of”(前面没空格),container的第一个值应该是“smallcases ”(后面有个空格);如果搜索字符串是“ of”(前面有空格),则container的第1个值是“small cases”(后面无空格)。
在扫描到指定字符串(搜索字符串)之后,scanner的位置指向了该字符串开始处。如果你想继续扫描该字符串之后的字符,必须先扫描指定字符串(搜索字符串)。下列代码演示了如何跳过搜索字串并取得产品类型。注意我们使用了substringFromIndex:,等同于继续扫描直到整个字符串的末尾。
NSString *product;
product = [[aScanner string] substringFromIndex:[aScanner scanLocation]];//用product接收未被扫描的余下的字符串
NSLog(@"%ld",[aScanner scanLocation]);
NSLog(@"product = %@",product);
例2 假设你有如下字符串:
Product: Acme Potato Peeler; Cost: 0.98 73
Product: Chef Pierre Pasta Fork; Cost: 0.75 19
Product: Chef Pierre Colander; Cost: 1.27 2
以下代码演示了读取产品名称和价格的操作(价格简单地读作一个float),跳过“Product:”和“Cost:”子串,以及分号。注意,因为scanner默认忽略空白字符和换行符,循环中没有指定对它们的处理(尤其对于读取末尾的整数而言,并不需要处理额外的空白字符)。
NSString *string = @"Product: Acme Potato Peeler; Cost: 0.98 73\n\
Product: Chef Pierre Pasta Fork; Cost: 0.75 19\n\
Product: Chef Pierre Colander; Cost: 1.27 2\n";
NSCharacterSet *semicolonSet;
NSScanner *theScanner;
NSString *PRODUCT = @"Product:";
NSString *COST = @"Cost:";
NSString *productName;
float productCost;
NSInteger productSold;
semicolonSet = [NSCharacterSet characterSetWithCharactersInString:@";"];
theScanner = [NSScanner scannerWithString:string];
while (![theScanner isAtEnd])
{
if ([theScanner scanString:PRODUCT intoString:NULL] && //从哪里开始扫描
[theScanner scanUpToCharactersFromSet:semicolonSet //扫描到哪里结束
intoString:&productName] && //存入哪个容器
[theScanner scanString:@";" intoString:NULL] &&
[theScanner scanString:COST intoString:NULL] &&
[theScanner scanFloat:&productCost] &&
[theScanner scanInteger:&productSold])
{
NSLog(@"Sales of %@: $%1.2f", productName, productCost * productSold);
}
}
//控制台输出 Sales of Acme Potato Peeler: $71.54
Sales of Chef Pierre Pasta Fork: $14.25
Sales of Chef Pierre Colander: $2.54