NSScanner 使用实例

NSScanner是用于扫描字符串内容的工具,其scanLocation属性至关重要。主要方法包括scanString:intoString:和scanUpToString:intoString:。在实际应用中,可以利用NSScanner查找并提取字符串中的特定信息,如从产品描述中解析出包装规格和数量。通过设置skipCharactersToBeSkipped,可以忽略特定字符。示例代码展示了如何从产品描述中提取名称和价格。

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
帮助我解释代码的逻辑 |// // LMVRKEBitmapParser.m // LMVInsensibilityBlueToothKey // // Created by van on 2025/9/24. // #import "LMVRKEBitmapParser.h" @implementation LMVRKEBitmapParser - (instancetype)initWithBitmapValue:(uint64_t)bitmapValue { self = [super init]; if (self) { _bitmapValue = bitmapValue; } return self; } - (instancetype)initWithHexString:(NSString *)hexString { self = [super init]; if (self) { _bitmapValue = [self parseHexString:hexString]; } return self; } #pragma mark - 解析方法 /// 解析16进制进制字符串 - (uint64_t)parseHexString:(NSString *)hexString { // 移除可能的前缀和空格 NSString *cleanHexString = [hexString stringByReplacingOccurrencesOfString:@"0x" withString:@""]; cleanHexString = [cleanHexString stringByReplacingOccurrencesOfString:@"0X" withString:@""]; cleanHexString = [cleanHexString stringByReplacingOccurrencesOfString:@" " withString:@""]; // 确保长度不超过16个字符 if (cleanHexString.length > 16) { cleanHexString = [cleanHexString substringToIndex:16]; } // 使用NSScanner转换 NSScanner *scanner = [NSScanner scannerWithString:cleanHexString]; uint64_t result = 0; [scanner scanHexLongLong:&result]; return result; } #pragma mark - 查询方法 - (BOOL)isFunctionEnabledAtBit:(NSUInteger)bitPosition { if (bitPosition >= 64) { return NO; } return (self.bitmapValue & ((uint64_t)1 << bitPosition)) != 0; } - (NSInteger)fetchBitForControlType:(LMVInsensibilityBluetooth_ControlType)controlType { switch (controlType) { case LMVInsensibilityBluetooth_WindowControl: return 3; case LMVInsensibilityBluetooth_FrunkControl: return 4; case LMVInsensibilityBluetooth_TrunkControl: return 5; case LMVInsensibilityBluetooth_Honks: return 6; case LMVInsensibilityBluetooth_RightSlidingDoor: return 7; case LMVInsensibilityBluetooth_LeftSlidingDoor: return 8; default: return 0; } } @end
09-26
这段代码实现了一个名为 `LMVRKEBitmapParser` 的类,主要用于解析和操作 **64位位图(bitmap)** 的值。位图是一种通过二进制位(bit)表示多个布尔状态的紧凑数据结构,常用于权限控制、功能开关等场景。以下是代码的详细逻辑解析: --- ### **1. 初始化方法** #### **(1) `initWithBitmapValue:`** ```objectivec - (instancetype)initWithBitmapValue:(uint64_t)bitmapValue { self = [super init]; if (self) { _bitmapValue = bitmapValue; // 直接存储传入的位图值 } return self; } ``` - **功能**:通过直接传入一个 `uint64_t` 类型的位图值初始化对象。 - **用途**:适用于已有位图数据(如从网络或数据库获取)的场景。 #### **(2) `initWithHexString:`** ```objectivec - (instancetype)initWithHexString:(NSString *)hexString { self = [super init]; if (self) { _bitmapValue = [self parseHexString:hexString]; // 调用解析方法 } return self; } ``` - **功能**:通过十六进制字符串(如 `"0x1A3F"`)初始化位图值。 - **流程**: 1. 调用 `parseHexString:` 方法清理字符串(移除前缀、空格)。 2. 将清理后的字符串转换为 `uint64_t` 数值。 --- ### **2. 核心解析方法** #### **`parseHexString:`** ```objectivec - (uint64_t)parseHexString:(NSString *)hexString { // 1. 清理字符串:移除 "0x"/"0X" 前缀和空格 NSString *cleanHexString = [hexString stringByReplacingOccurrencesOfString:@"0x" withString:@""]; cleanHexString = [cleanHexString stringByReplacingOccurrencesOfString:@"0X" withString:@""]; cleanHexString = [cleanHexString stringByReplacingOccurrencesOfString:@" " withString:@""]; // 2. 限制长度:最多解析16个字符(对应64位十六进制数) if (cleanHexString.length > 16) { cleanHexString = [cleanHexString substringToIndex:16]; } // 3. 使用 NSScanner 将十六进制字符串转换为 uint64_t NSScanner *scanner = [NSScanner scannerWithString:cleanHexString]; uint64_t result = 0; [scanner scanHexLongLong:&result]; return result; } ``` - **关键点**: - **输入处理**:支持带 `0x` 前缀或空格的字符串(如 `"0x 1A 3F"`)。 - **安全性**:截断超长字符串,避免溢出。 - **转换工具**:`NSScanner` 的 `scanHexLongLong:` 方法直接解析十六进制字符串。 --- ### **3. 查询方法** #### **(1) `isFunctionEnabledAtBit:`** ```objectivec - (BOOL)isFunctionEnabledAtBit:(NSUInteger)bitPosition { if (bitPosition >= 64) { return NO; // 防止越界(uint64_t 只有0-63位) } return (self.bitmapValue & ((uint64_t)1 << bitPosition)) != 0; } ``` - **功能**:检查位图中某一位是否为 `1`(表示功能启用)。 - **原理**: 1. 通过位运算 `(1 << bitPosition)` 生成一个只有目标位为 `1` 的掩码。 2. 与 `bitmapValue` 按位与,判断结果是否非零。 - **示例**: - 若 `bitmapValue = 0b1010`(十进制 `10`),检查第1位(从0开始): ```objectivec [parser isFunctionEnabledAtBit:1]; // 返回 YES(1 << 1 = 0b10,与 0b1010 按位与得 0b10) ``` #### **(2) `fetchBitForControlType:`** ```objectivec - (NSInteger)fetchBitForControlType:(LMVInsensibilityBluetooth_ControlType)controlType { switch (controlType) { case LMVInsensibilityBluetooth_WindowControl: return 3; case LMVInsensibilityBluetooth_FrunkControl: return 4; case LMVInsensibilityBluetooth_TrunkControl: return 5; case LMVInsensibilityBluetooth_Honks: return 6; case LMVInsensibilityBluetooth_RightSlidingDoor: return 7; case LMVInsensibilityBluetooth_LeftSlidingDoor: return 8; default: return 0; // 未知类型返回第0位(可能表示禁用) } } ``` - **功能**:将枚举类型的控制指令(如车窗、后备箱)映射到位图中的具体位位置。 - **设计意图**: - 硬编码位位置,便于直接调用 `isFunctionEnabledAtBit:` 检查功能状态。 - 例如:检查“车窗控制”是否启用: ```objectivec NSUInteger bitPos = [parser fetchBitForControlType:LMVInsensibilityBluetooth_WindowControl]; BOOL isEnabled = [parser isFunctionEnabledAtBit:bitPos]; ``` --- ### **4. 代码用途推测** - **应用场景**:可能是车载蓝牙钥匙系统中,通过位图管理不同硬件功能的权限(如哪些控制功能对当前钥匙可用)。 - **数据流**: 1. 从服务器或本地存储获取十六进制字符串或位图值。 2. 初始化 `LMVRKEBitmapParser` 对象。 3. 通过 `fetchBitForControlType:` 和 `isFunctionEnabledAtBit:` 查询具体功能是否启用。 --- ### **5. 潜在改进点** 1. **错误处理**:`parseHexString:` 可增加对非法字符(如 `"G"`)的检查。 2. **动态配置**:位位置(如 `WindowControl` 对应第3位)可通过外部配置文件定义,避免硬编码。 3. **线程安全**:如果位图可能被多线程修改,需加锁保护 `_bitmapValue`。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值