Swift 学习日记

Swift 中 NSString 与 String 的认识

2018-03-15  本文已影响193人  tp夕阳武士

在Swift语法中相对OC新增了一个修辞词:String,它与原来的NSString可以很方便的转换,但是在实际的开发过程中我们应该如何选择?

1.能用String的时候就使用String

(1).因为现在所有的Cocoa框架都能接收和也能返回String类型,所以没有必要特地转换
(2).Swift中String是struct 而 NSString是NSObject,所以String更符合字符串不变的特性;同时在不触及NSString的特有操作的情况下,使用String的方法也有性能的提升:
(3).由于String类型实现了Collection type的这样的接口,所以某些Swift的特性只有String类型能用,而NSString却没有;

例如 for ... in 的枚举遍历所有字符:

let words: String = "ILOVEYOU"
for character in words{
      printf(charcter)
}

2.要使用NSString的情况

(1).String类型有hasPrefix/hasSuffix方法来判断是否以某字符串开头或者结尾;

if words.hasPrefix("ILOVE") {
            print("YES")
        }else{
            print("NO")
        }
//以上代码输出结果为YES 

NSString类型中虽然没有这种方法,但是NSString类型中却有containsString方法来判断字符串内部是否包含了某些字符,目前4.0版本containsString方法被缩写为:contains

 //String类型转换成NSString类型
let nsWords = (words as NSString)
print(nsWords)
if nsWords.contains("ILOVE") {
      NSLog("YES")
}else{
      NSLog("NO")
}
//输出结果YES

(2). String类型截取字段的时候比较麻烦

//NSString类型截取字段的方法(Swift4.0)
let rangeStr1 = (words as NSString).substring(with: NSMakeRange(4, 2))

//String类型截取字段的方法(现在这个方法在Swift4.0中已被取消)
let index =  words.startIndex.advancedBy(4)
let index2 = words.startIndex.advancedBy(6)
let range = Range<String.Index>(start: index, end: index2)
let rangeStr2 = words.substringWithRange(range) 

字符串的字面量

你可以在代码里使用一段预定义的字符串值作为字符串字面量。字符串字面量是由一对双引号包裹着的具有固定顺序的字符集。

//字符串字面量可以用于为常量和变量提供初始值:
let someString = "Some string literal value"
//someString 是一个字符串常量,通过字符串字面量进行初始化,Swift语法系统会自动推断它为String类型

多行字符串字面量的书写格式

let quotation = """
The White Rabbit put on his spectacles.  "Where shall I begin,
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""
//注意:上面这段代码中,在两个 ' """ ' 中包含着一个多行字符串的字面量
// 字符串中还可夹杂双引号,需要分行是直接用回车符号,非常好用.
图解: 多行字符串.png

字符串字面量的特殊字符

//示例代码
let str1 = "\u{24}"
print("str1:\(str1) str1")
//输出结果 :str1:$ str1

let str2 = "\0"
print("str2:\(str2) str2")
//输出结果 :str2: str2

let str3 = "\\"
print("str3:\(str3) str3")
//输出结果:str3:\ str3

let str4 = "\t"
print("str4:\(str4) str4")
//输出结果:str4:     str4

let str5 = "\n"
print("str5:\(str5) str5")
//输出结果:str5:
 str5

let str6 = "\n"
print("str6:\(str6) str6")
//输出结果: str6:
 str6

let str7 = "\"abc\""
print("str7:\(str7) str7")
//输出结果:str7:"abc" str7

let str8 = "\'abc\'"
print("str9 \(str8) str9")
//输出结果:str9 'abc' str9        

字符与字符串

您可通过for-in循环来遍历字符串,获取字符串中每一个字符的值:

for character in "Dog!🐶" {
    print(character)
}
// D
// o
// g
// !
// 🐶

另外,通过标明一个Character类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:

let exclamationMark: Character = "!"

字符串可以通过传递一个Character的数组作为自变量来初始化:

let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
print(catString)
// 打印输出:"Cat!🐱"

String类型的拼接

let hello = "Hello,"
let world  = "World"
let character: Character = "!"

//过加法运算符(+)相加在一起
var hellworld = hell + world // hellword 现在等于 "Hello,World"

//可以用append()方法将一个字符附加到一个字符串变量的尾部
hellworld.append(character) // hellworld 现在等于 "Hello,World!"

//也可以通过加法赋值运算符 (+=) 将一个字符串添加到一个已经存在字符串变量上:
let fuck = " fuck"
hellworld += fuck //hellworld 现在等于 "Hello,World! fuck" 

注意:
您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。这个有点像 Int.max 不能再加 1

字符串插值

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"

获取字符串的字符数量

//获取字符串的字符数量直接使用String自带的方法.count
let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusualMenagerie has \(unusualMenagerie.count) characters")
// 打印输出 "unusualMenagerie has 40 characters"
  • 注意在 Swift 中,使用可拓展的字符群集作为Character值来连接或改变字符串时,并不一定会更改字符串的字符数量。
var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// 打印输出 "the number of characters in cafe is 4"

word += "\u{301}"    // 拼接一个重音, U+0301  得到 word = café

print("the number of characters in \(word) is \(word.count)")
// 打印输出 "the number of characters in café is 4"

Unicode

1). 什么是Unicode?

Unicode是一个国际标准,用于文本的编码和表示。 它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。 Swift 的String和Character类型是完全兼容 Unicode 标准的。

2). Unicode的标量

Unicode 标量是对应字符或者修饰符的唯一的21位数字:

//例如U+0061表示小写的拉丁字母(LATIN SMALL LETTER A)("a")
//U+1F425表示小鸡表情(FRONT-FACING BABY CHICK) ("🐥")
let character = "\u{0061}" //  character 的值是 "a"
let monkey = "\u{1f435}" // monkey 的值是 "🐵"
  • 注意: Unicode 码位(code poing) 的范围是U+0000到U+D7FF或者U+E000到U+10FFFF。Unicode 标量不包括 Unicode 代理项(surrogate pair) 码位,其码位范围是U+D800到U+DFFF。
  • 不是所有的21位 Unicode 标量都代表一个字符,因为有一些标量是留作未来分配的。
可扩展的字形群集

每一个 Swift 的Character类型代表一个可扩展的字形群。 一个可扩展的字形群是一个或多个 Unicode 标量的有序排列。

//举例
//字母é 可以是一个Unicode标量 "\u{00E9}",
//也可以是一对标量:"\u{0065}\u{0301}"
//在写代码时,前面的0都可以不谢
let e1 = "\u{E9}" // e1 = é 
let e2 = "\u{65}\u{301}" // e2 = é 
//并且 e1.count == e2.count 

//可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的Character值。
//来自朝鲜语字母表的韩语音节能表示为组合或分解的有序排列
//在 Swift 都会表示为同一个单一的Character值:

let precomposed: Character = "\u{D55C}"                  // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ᄒ, ᅡ, ᆫ
// precomposed 是 한, decomposed 是 한

//可拓展的字符群集可以使包围记号(l例如:U+20DD)的标量包围其他 Unicode 标量:
let enclosedEAcute: Character = "\u{E9}\u{20DD}"// enclosedEAcute 是 é⃝
let myFirstName: Character = "庞\u{20DD}"// 日 是 庞⃝

//地域性指示符号的 Unicode 标量可以组合成一个单一的Character值:
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS 是 🇺🇸

修改和访问String

String 类型的:索引

每一个String类型值都有一个关联的索引(index),String.Index,它对应着Sting中的每一个Chracter的位置。
但是不同的Chracter可能占用着不同的内存空间,所以要想知道Chracter的确定位置,要从String的开头遍历每一个Unicode,因此String的索引不能用整数,例如:

let str1: String = "\u{65}\u{301}" //等于 é
print(a.startIndex)
//ndex(_compoundOffset: 0, _cache: Swift.String.Index._Cache.utf16) 
print(a.endIndex)
//Index(_compoundOffset: 8, _cache: Swift.String.Index._Cache.utf16)

使用startIndex属性可以获取一个String的第一个Character的索引。使用endIndex属性可以获取最后一个Character的后一个位置的索引。因此,endIndex属性不能作为一个字符串的有效下标。如果String是空串,startIndex和endIndex是相等的。
通过调用 String 的 index(before:) 或 index(after:) 方法,可以立即得到前面或后面的一个索引。您还可以通过调用 index(_:offsetBy:) 方法来获取对应偏移量的索引,这种方式可以避免多次调用 index(before:) 或 index(after:) 方法。

//定义一个String类型的常量str,这个由3个Chracter组成a / b / ć
let str = "abc\u{301}" //abć
//获取 str中的第一位Chracter
let firstChracter =   str[str.startIndex] //注意这里使用的是中括号
// 输出 a
let secondChracter = str[str.index(after: str.startIndex)]
//输出 b
let thirdChracter = str[str.index(before:庞日富.endIndex)]
//输出 c
let index = str.index(str.startIndex, offsetBy: str.count - 1)
let lastChracter = str[indx]
//输出 ć

视图获取越界的索引或者越界的索引值都会导致奔溃

greeting[greeting.endIndex] // error
greeting.index(after: endIndex) // error

使用 indices 属性会创建一个包含全部索引的范围(Range),用来在一个字符串中访问单个字符。

let abc = "abc" 
for chracter in abc{
      print( chracter )
}
//输出:
//a
//b
//c

for index in abc.indices{
       print(abc[index])
}
//输出:
//a
//b
//c
插入和删除

调用 insert(_:at:) 方法可以在一个字符串的指定索引插入一个字符,调用 insert(contentsOf:at:) 方法可以在一个字符串的指定索引插入一个段字符串。

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 变量现在等于 "hello!"

welcome.insert(contentsOf:" there", at: welcome.index(before: welcome.endIndex))
// welcome 变量现在等于 "hello there!"

调用 remove(at:) 方法可以在一个字符串的指定索引删除一个字符,调用 removeSubrange(_:) 方法可以在一个字符串的指定索引删除一个子字符串。

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 现在等于 "hello there"

let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome 现在等于 "hello"

注意: 您可以使用 insert(:at:)、insert(contentsOf:at:)、remove(at:) 和 removeSubrange(:) 方法在任意一个确认的并遵循 RangeReplaceableCollection 协议的类型里面,如上文所示是使用在 String 中,您也可以使用在 Array、Dictionary 和 Set 中。

子字符串

当你从字符串中获取一个子字符串 —— 例如,使用下标或者 prefix(_:) 之类的方法 —— 就可以得到一个 SubString 的实例,而非另外一个 String。Swift 里的 SubString 绝大部分函数都跟 String 一样,意味着你可以使用同样的方式去操作 SubString 和 String。然而,跟 String 不同的是,你只有在短时间内需要操作字符串时,才会使用 SubString。当你需要长时间保存结果时,就把 SubString 转化为 String 的实例:

let greeting = "Hello, world!"
let index = greeting.index(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index] //beginning在这里不是String类型,而是SubSquence
// beginning 的值为 "Hello"

// 把结果转化为 String 以便长期存储。
let newString = String(beginning)

String一样,每一个SubString都会在内存里保存字符集,StringSubString的区别在于性能优化上,SubString可以重用原String的内存空间,或者另一个SubString的内存空间,(String也有同样的优化,但是如果两个String使用同样的内存的话,他们就会相等),着意味着子修改StringSubString之前都不需要消耗性能去赋值内存,SubString不适合长期储存,因为它重用了Stirng的内存空间,元String的内存空间必须保留,知道它的SubString不再被使用。

//案例
let greeting:String = "Hello,World!"
//意味着它在内存里有一片空间保存字符集
let index = greeting.index(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
//beginning 是 greeting 的 SubString ,所以它重用了greenting的内存
let newString = String(beginning) 
//newString 是使用 beginning (beginning是一个SubString)创建的,它拥有自己的内存,

greeting beginning newString的关系可以用下图解释:

stringSubstring.png

比较字符串

Swift 提供了三种方式来比较文本值:字符串字符相等、前缀相等和后缀相等。

字符串/字符相等
// 用 == 运算符进行判断
let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
    print("These two strings are considered equal")
}
// 打印输出 "These two strings are considered equal"

//如果两个字符串 print出来的值是相等的,我们就认为他是相等的
let strA = "\u{E9}" // é
let strB = "\u{65}\u{301}" //  é

if strA == strB {
    print( " strA 和 strB 是相等的" ) //代码会执行到这里来
}

注意:
在 Swift 中,字符串和字符并不区分地域(not locale-sensitive)。

前缀/后缀相等

通过调用字符串的hasPrefix(:)/hasSuffix(:)方法来判断字符串是否拥有指定的 前缀/后缀,两个方法均接收一个String类型的参数,并返回一个布尔值。

let romeoAndJuliet = [
    "Act 1 Scene 1: Verona, A public place",
    "Act 1 Scene 2: Capulet's mansion",
    "Act 1 Scene 3: A room in Capulet's mansion",
    "Act 1 Scene 4: A street outside Capulet's mansion",
    "Act 1 Scene 5: The Great Hall in Capulet's mansion",
    "Act 2 Scene 1: Outside Capulet's mansion",
    "Act 2 Scene 2: Capulet's orchard",
    "Act 2 Scene 3: Outside Friar Lawrence's cell",
    "Act 2 Scene 4: A street in Verona",
    "Act 2 Scene 5: Capulet's mansion",
    "Act 2 Scene 6: Friar Lawrence's cell"
]

//统计 Act1 前缀出现的次数
var act1SceneCount = 0 
for scene in romeoAndJuliet {
    if scene.hasPrefix("Act 1 ") {
        act1SceneCount += 1
    }
}
print("There are \(act1SceneCount) scenes in Act 1")
// 打印输出 "There are 5 scenes in Act 1"

//统计后缀:Capulet's mansion 和后缀:Friar Lawrence's cell分别出现的次数
var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
    if scene.hasSuffix("Capulet's mansion") {
        mansionCount += 1
    } else if scene.hasSuffix("Friar Lawrence's cell") {
        cellCount += 1
    }
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// 打印输出 "6 mansion scenes; 2 cell scenes"

上一篇下一篇

猜你喜欢

热点阅读