我们在使用标准的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
|
|