添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

我们在使用标准的JSON或者XML时可以非常容易的进行解析并获取到想要的数据,但是在对一些不那么规则或自定义的字符串进行处理时,就显得比较麻烦了,幸好iOS提供了Scanner类和对正则表达式的支持(后续在写)。Scanner类是一个类簇的抽象父类,该类簇为一个从NSString(虽然Swift中大多使用String,但这个类还保留着NSString)对象扫描值的对象提供了程序接口。

先来查看Scanner的定义:

1
2
3
4
5
6
7
8
open class Scanner : NSObject , NSCopying {
open var string : String { get } // 被扫描的字符串
open var scanLocation : Int // 扫描到了某个位置,默认0,下次扫描会从这里开始,如果超出了string的range,将会NSRangeException
open var charactersToBeSkipped : CharacterSet? // 需要忽略掉的字符集,很多资料上说会自动忽略空格和换行符,但实测并不会自动忽略,所以现在大部分的博客可能都不太正确,"\n\t"这两个东西还是需要手动设置的。
open var caseSensitive : Bool // default is false, 默认不区分大小写。另外这个设置不适用于charactersToBeSkipped,例如要忽略字符所有的元音字幕,需要设置忽略"AEIOUaeiou"
open var locale : Any ? // 多在国际化多语言的时候使用,例如Locale(identifier: "zh_CN"), 主要对数字,日期时间等产生影响
public init ( string : String ) // 通过需要被扫描的字符串初始化

一些个扫描的方法
这些方法都是扫描到正确的内容后返回true并设置scanLocation,否则返回false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 扫描整数
// 扫描Int32并存入相应指针, 所有结果都有效,上溢出为Int32.max, 下溢出为Int32.min
open func scanInt32 ( _ result : UnsafeMutablePointer < Int32 > ? ) -> Bool
// 扫描Int并存入相应指针, 所有结果都有效,这里比较特殊,Swift中32位系统下Int == Int32, 64位系统下Int == Int64
open func scanInt ( _ result : UnsafeMutablePointer < Int > ? ) -> Bool
// 扫描Int64并存入相应指针,同32
open func scanInt64 ( _ result : UnsafeMutablePointer < Int64 > ? ) -> Bool
// 扫描UnsignedLongLong并存入相应指针 UInt64,Swift中UnsignedLongLong == UInt64
open func scanUnsignedLongLong ( _ result : UnsafeMutablePointer < UInt64 > ? ) -> Bool
// 扫描浮点数, 上溢出HUGE,下溢出-HUGE,都是有效的,不会crash
open func scanFloat ( _ result : UnsafeMutablePointer < Float > ? ) -> Bool
open func scanDouble ( _ result : UnsafeMutablePointer < Double > ? ) -> Bool
// 扫描十六进制数字到 整数,都可以不带"0x" 或者 "0X"前缀,以%a或者%A格式化
open func scanHexInt32 ( _ result : UnsafeMutablePointer < UInt32 > ? ) -> Bool
open func scanHexInt64 ( _ result : UnsafeMutablePointer < UInt64 > ? ) -> Bool
// 扫描十六进制数字到 浮点数,必须带上带"0x" 或者 "0X"前缀,以%a或者%A格式化
open func scanHexFloat ( _ result : UnsafeMutablePointer < Float > ? ) -> Bool
open func scanHexDouble ( _ result : UnsafeMutablePointer < Double > ? ) -> Bool
// 扫描匹配的字符串,找到第一个后会停止,并设置scanLocation
open func scanString ( _ string : String , into result : AutoreleasingUnsafeMutablePointer < NSString ? > ? ) -> Bool
// 扫描匹配的字符集,找到第一个后会停止,并设置scanLocation
open func scanCharacters ( from set : CharacterSet , into result : AutoreleasingUnsafeMutablePointer < NSString ? > ? ) -> Bool
// 扫描到匹配的字符串后结束,并设置scanLocation
open func scanUpTo ( _ string : String , into result : AutoreleasingUnsafeMutablePointer < NSString ? > ? ) -> Bool
// 扫描到匹配的字符集后结束,并设置scanLocation
open func scanUpToCharacters ( from set : CharacterSet , into result : AutoreleasingUnsafeMutablePointer < NSString ? > ? ) -> Bool
// 这个在扫描货币或者其他精度要求特别高的数字时使用,Decimal相关的使用后续再写
open func scanDecimal ( _ dcm : UnsafeMutablePointer < Decimal > ? ) -> Bool

说了这么多,来一些实战使用

示例1:找出一段字符中的所有数字,并用数组存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var str = "Hel2lo,playgr3ound9527 6674"
let scanner = Scanner ( string : str ) // 创建Scanner
scanner. caseSensitive = true // 区分大小写
var intResult = 0
scanner. scanInt ( & intResult ) // 这里由于第一个并不是数字,所有扫描不到结果
var scanLocation = 0
var resultArray : [ String ] = [ String ] ( )
while scanner. isAtEnd != true {
var stringResult : NSString ? = nil
if scanner. scanCharacters ( from : CharacterSet. decimalDigits , into : & stringResult ) {
print ( "result" , scanner. scanLocation )
resultArray. append ( ( stringResult ! as String ) )
else {
print ( "no result" , scanner. scanLocation )
scanner. scanLocation += 1 // 这里是如果没有扫描到,就把location后移一位,继续扫描
print ( resultArray )

示例2:从一个非常规格式字符串中匹配出需要的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
let productAndCostStr = " \t Product: Acme Potato Peeler; Cost: 0.98 73 \n \t Product: Chef Pierre Pasta Fork; Cost: 0.75 19 \n \t Product: Chef Pierre Colander; Cost: 1.27 2"
let PRODUCT = "Product"
let COST = "Cost"
let semicolon = "; "
class ProductModel {
public init ( ) {
open var productName : NSString ?
open var productCost : NSString ?
open var floatValue : Double = 0.0
open var intValue : Int = 0
func description ( ) -> String {
return "productName = \( productName ?? " nil ") \n productCost = \( productCost ?? " nil ") \n floatValue = \( floatValue) \n intValue = \( intValue)"
let tempScanner = Scanner ( string : productAndCostStr )
let model = ProductModel ( )
let semicolonSet = CharacterSet ( charactersIn : ";" )
tempScanner. charactersToBeSkipped = CharacterSet ( charactersIn : " : \n \t " )
while tempScanner. isAtEnd == false {
if tempScanner. scanString ( PRODUCT , into : nil ) &&
tempScanner. scanUpToCharacters ( from : semicolonSet , into : & model. productName ) &&
tempScanner. scanString ( ";" , into : nil ) &&
tempScanner. scanString ( COST , into : & model. productCost ) &&
tempScanner. scanDouble ( & model. floatValue ) &&
tempScanner. scanInt ( & model. intValue ) {
print ( model. description ( ) )

很多时候由于正则表达式在使用上的方便性,我们忽略了Scanner,但Scanner提供了另外一种很好很直观且安全的判断和取值方式,两个结合才能相得益彰。

以上测试内容的playground下载

学习笔记 , 技术原创 permalink