Swift 字符串(String)

demo地址:https://share.weiyun.com/5xZIuyy

Swift 字符串是一系列字符的集合。例如 "Hello, World!" 這樣的有序的字符類(lèi)型的值的集合,它的數(shù)據(jù)類(lèi)型為 String。

創(chuàng)建字符串

你可以通過(guò)使用字符串字面量或 String 類(lèi)的實(shí)例來(lái)創(chuàng)建一個(gè)字符串:

// 使用字符串字面量
var stringA = "Hello, World!"
print( stringA )

// String 實(shí)例化
var stringB = String("Hello, World!")
print( stringB )

以上程序執(zhí)行輸出結(jié)果為:

Hello, World!
Hello, World!

空字符串

你可以使用空的字符串字面量賦值給變量或初始化一個(gè)String類(lèi)的實(shí)例來(lái)初始值一個(gè)空的字符串。 我們可以使用字符串屬性 isEmpty 來(lái)判斷字符串是否為空:

// 使用字符串字面量創(chuàng)建空字符串
var stringA = ""

if stringA.isEmpty {
    print( "stringA 是空的" )
} else {
    print( "stringA 不是空的" )
}
        
// 實(shí)例化 String 類(lèi)來(lái)創(chuàng)建空字符串
let stringB = String()
        
if stringB.isEmpty {
    print( "stringB 是空的" )
} else {
    print( "stringB 不是空的" )
}

以上程序執(zhí)行輸出結(jié)果為:

stringA 是空的
stringB 是空的

判斷字符串是否全為空格

    //判斷字符串是否全為空格
    func isAllEmpty(str:String?) -> Bool
    {
        var isEmpty = false
        let tempString = str ?? ""
        
        if tempString.count == 0
        {
            isEmpty = true
            
        }else
        {
            // 創(chuàng)建一個(gè)字符集對(duì)象, 包含所有的空格和換行字符
            let characterSet = NSCharacterSet.whitespacesAndNewlines
            // 從字符串中過(guò)濾掉首尾的空格和換行, 得到一個(gè)新的字符串
            let trimedString = tempString.trimmingCharacters(in: characterSet)
            // 判斷新字符串的長(zhǎng)度是否為0
            if trimedString.count == 0
            {
                isEmpty = true
            }
        }
        return isEmpty
    }

去掉首尾空格

    //去掉首尾空格,lineFeed:true 表示包含換行符
    func removeHeadAndTailSpace(str:String?, lineFeed:Bool) -> String
    {
        let tempString = str ?? ""
        var whitespace = CharacterSet()
        if lineFeed == true
        {
            whitespace = NSCharacterSet.whitespacesAndNewlines
            
        }else
        {
            whitespace = NSCharacterSet.whitespaces
        }
        
        return tempString.trimmingCharacters(in: whitespace)
    }

去掉所有空格

    //去掉所有空格,lineFeed:true 表示包含換行符
    func removeAllSpace(str:String?, lineFeed:Bool) -> String
    {
        let tempString = str ?? ""
        
        if tempString.count == 0
        {
            return tempString
        }

        if lineFeed == false
        {
            return tempString.replacingOccurrences(of: " ", with: "")
            
        }else
        {
            let characterSet = NSCharacterSet.whitespacesAndNewlines
            let trimedString = tempString.trimmingCharacters(in: characterSet)
            return trimedString.replacingOccurrences(of: " ", with: "")
        }
    }

字符串常量

你可以將一個(gè)字符串賦值給一個(gè)變量或常量,變量是可修改的,常量是不可修改的。

import Cocoa

// stringA 可被修改
var stringA = "菜鳥(niǎo)教程:"
stringA += "http://www.runoob.com"
print( stringA )

// stringB 不能修改
let stringB = String("菜鳥(niǎo)教程:")
stringB += "http://www.runoob.com"
print( stringB )

以上程序執(zhí)行輸出結(jié)果會(huì)報(bào)錯(cuò),因?yàn)?stringB 為常量是不能被修改的:

error: left side of mutating operator isn't mutable: 'stringB' is a 'let' constant
stringB += "http://www.runoob.com"

字符串字面量

你可以在代碼里使用一段預(yù)定義的字符串值作為字符串字面量。字符串字面量是由一對(duì)雙引號(hào)包裹著的具有固定順序的字符集。字符串字面量可以用于為常量和變量提供初始值:

let someString = "Some string literal value"

注意someString常量通過(guò)字符串字面量進(jìn)行初始化,Swift 會(huì)推斷該常量為String類(lèi)型。

多行字符串字面量

如果你需要一個(gè)字符串是跨越多行的,那就使用多行字符串字面量 —— 由一對(duì)三個(gè)雙引號(hào)包裹著的具有固定順序的文本字符集:

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."
"""

一個(gè)多行字符串字面量包含了所有的在開(kāi)啟和關(guān)閉引號(hào)(""")中的行。這個(gè)字符從開(kāi)啟引號(hào)(""")之后的第一行開(kāi)始,到關(guān)閉引號(hào)(""")之前為止。這就意味著字符串開(kāi)啟引號(hào)之后(""")或者結(jié)束引號(hào)(""")之前都沒(méi)有換行符號(hào)。(譯者:下面兩個(gè)字符串其實(shí)是一樣的,雖然第二個(gè)使用了多行字符串的形式)

let singleLineString = "These are the same."
let multilineString = """
These are the same.
"""

如果你的代碼中,多行字符串字面量包含換行符的話,則多行字符串字面量中也會(huì)包含換行符。如果你想換行,以便加強(qiáng)代碼的可讀性,但是你又不想在你的多行字符串字面量中出現(xiàn)換行符的話,你可以用在行尾寫(xiě)一個(gè)反斜杠(\)作為續(xù)行符。

let softWrappedQuotation = """
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."
"""

為了讓一個(gè)多行字符串字面量開(kāi)始和結(jié)束于換行符,請(qǐng)將換行寫(xiě)在第一行和最后一行,例如:

let lineBreaks = """

This string starts with a line break.
It also ends with a line break.

"""

字符串字面量的特殊字符

字符串字面量可以包含以下特殊字符:

  • \0(空字符)、\\(反斜線)、\t(水平制表符)、\n(換行符)、\r(回車(chē)符)、\"(雙引號(hào))、\'(單引號(hào))
  • Unicode 標(biāo)量,寫(xiě)成\u{n}(u為小寫(xiě)),其中n為任意一到八位十六進(jìn)制數(shù)且可用的 Unicode 位碼
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imageination is more important than knowledge" - Enistein
let dollarSign = "\u{24}"             // $, Unicode 標(biāo)量 U+0024
let blackHeart = "\u{2665}"           // ?, Unicode 標(biāo)量 U+2665
let sparklingHeart = "\u{1F496}"      // ??, Unicode 標(biāo)量 U+1F496

由于多行字符串字面量使用了三個(gè)雙引號(hào),而不是一個(gè),所以你可以在多行字符串字面量里直接使用雙引號(hào)(")而不必加上轉(zhuǎn)義符(\)。要在多行字符串字面量中使用 """ 的話,就需要使用至少一個(gè)轉(zhuǎn)義符(\):下面的代碼為各種特殊字符的使用示例。 wiseWords常量包含了兩個(gè)雙引號(hào)。 dollarSign、blackHeart和sparklingHeart常量演示了三種不同格式的 Unicode 標(biāo)量:

let threeDoubleQuotes = """
Escaping the first quote \"""
Escaping all three quotes \"\"\"
"""

字符串長(zhǎng)度

字符串長(zhǎng)度使用 .count 屬性來(lái)計(jì)算

  func stringLength()
  {
      var varA   = "www.runoob.com"
      
      print( "\(varA), 長(zhǎng)度為 \(varA.count)" )
  }

以上程序執(zhí)行輸出結(jié)果為:

www.runoob.com, 長(zhǎng)度為 14

注意:在 Swift 中,使用可拓展的字符群集作為Character值來(lái)連接或改變字符串時(shí),并不一定會(huì)更改字符串的字符數(shù)量。

例如,如果你用四個(gè)字符的單詞cafe初始化一個(gè)新的字符串,然后添加一個(gè)COMBINING ACTUE ACCENT(U+0301)作為字符串的結(jié)尾。最終這個(gè)字符串的字符數(shù)量仍然是4,因?yàn)榈谒膫€(gè)字符是é,而不是e:

var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// 打印輸出 "the number of characters in cafe is 4"

word += "\u{301}"    // 拼接一個(gè)重音, U+0301

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

注意:可擴(kuò)展的字符群集可以組成一個(gè)或者多個(gè) Unicode 標(biāo)量。這意味著不同的字符以及相同字符的不同表示方式可能需要不同數(shù)量的內(nèi)存空間來(lái)存儲(chǔ)。所以 Swift 中的字符在一個(gè)字符串中并不一定占用相同的內(nèi)存空間數(shù)量。因此在沒(méi)有獲取字符串的可擴(kuò)展的字符群的范圍時(shí)候,就不能計(jì)算出字符串的字符數(shù)量。如果您正在處理一個(gè)長(zhǎng)字符串,需要注意count屬性必須遍歷全部的 Unicode 標(biāo)量,來(lái)確定字符串的字符數(shù)量。另外需要注意的是通過(guò)count屬性返回的字符數(shù)量并不總是與包含相同字符的NSString的length屬性相同。NSString的length屬性是利用 UTF-16 表示的十六位代碼單元數(shù)字,而不是 Unicode 可擴(kuò)展的字符群集。

字符串是值類(lèi)型

Swift 的String類(lèi)型是值類(lèi)型。 如果您創(chuàng)建了一個(gè)新的字符串,那么當(dāng)其進(jìn)行常量、變量賦值操作,或在函數(shù)/方法中傳遞時(shí),會(huì)進(jìn)行值拷貝。 任何情況下,都會(huì)對(duì)已有字符串值創(chuàng)建新副本,并對(duì)該新副本進(jìn)行傳遞或賦值操作。 值類(lèi)型在 結(jié)構(gòu)體和枚舉是值類(lèi)型 中進(jìn)行了詳細(xì)描述。

Swift 默認(rèn)字符串拷貝的方式保證了在函數(shù)/方法中傳遞的是字符串的值。 很明顯無(wú)論該值來(lái)自于哪里,都是您獨(dú)自擁有的。 您可以確信傳遞的字符串不會(huì)被修改,除非你自己去修改它。

在實(shí)際編譯時(shí),Swift 編譯器會(huì)優(yōu)化字符串的使用,使實(shí)際的復(fù)制只發(fā)生在絕對(duì)必要的情況下,這意味著您將字符串作為值類(lèi)型的同時(shí)可以獲得極高的性能。

使用字符

您可通過(guò)for-in循環(huán)來(lái)遍歷字符串,獲取字符串中每一個(gè)字符的值:

for character in "Dog!??" {
    print(character)
}

以上程序輸出的結(jié)果是:

D
o
g
!
??

另外,通過(guò)標(biāo)明一個(gè)Character類(lèi)型并用字符字面量進(jìn)行賦值,可以建立一個(gè)獨(dú)立的字符常量或變量:

let exclamationMark: Character = "!"

字符串可以通過(guò)傳遞一個(gè)值類(lèi)型為Character的數(shù)組作為自變量來(lái)初始化:

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

字符串全部大寫(xiě)/小寫(xiě)

使用 String.lowercased()將字符串中字母全部轉(zhuǎn)化為小寫(xiě)字母;使用String.uppercased()將字符串中字母全部轉(zhuǎn)化為大寫(xiě)字母。

 let stringBase = "aBcDefG"
 print(stringBase.lowercased())  //abcdefg
 print(stringBase.uppercased())  //ABCDEFG

字符串分割

使用String.components(separatedBy:)將字符(串)按照特定的分割符分割,分割后的結(jié)果放在數(shù)組中;使用Array.joined(separator:)將數(shù)組中字符(串)組合成一個(gè)字符串。

let strNumber = "0,1,2,3,4,5,6,7,8,9"
let strArray = strNumber.components(separatedBy: ",")
print(strArray)  //["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
        
let strJoin = strArray.joined(separator: " - ")
print(strJoin)  //0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9

字符串中插入值

字符串插值是一種構(gòu)建新字符串的方式,可以在其中包含常量、變量、字面量和表達(dá)式。 您插入的字符串字面量的每一項(xiàng)都在以反斜線為前綴的圓括號(hào)中:

    func insertValue()
    {
        var varA   = 20
        let constA = 100
        var varC:Float = 20.0
        
        var stringA = "\(varA) 乘于 \(constA) 等于 \(varC * 100)"
        print( stringA )
    }

以上程序執(zhí)行輸出結(jié)果為:

20 乘于 100 等于 2000.0

注意:插值字符串中寫(xiě)在括號(hào)中的表達(dá)式不能包含非轉(zhuǎn)義反斜杠 (\),并且不能包含回車(chē)或換行符。不過(guò),插值字符串可以包含其他字面量。

字符串連接

字符串可以通過(guò) + 號(hào)來(lái)連接

    func jointString()
    {
        let constA = "菜鳥(niǎo)教程:"
        let constB = "http://www.runoob.com"
        
        var stringA = constA + constB
        
        print( stringA )
    }

以上程序執(zhí)行輸出結(jié)果為:

菜鳥(niǎo)教程:http://www.runoob.com

您也可以通過(guò)加法賦值運(yùn)算符 (+=) 將一個(gè)字符串添加到一個(gè)已經(jīng)存在字符串變量上:

var instruction = "look over"
instruction += string2
// instruction 現(xiàn)在等于 "look over there"

您可以用append()方法將一個(gè)字符附加到一個(gè)字符串變量的尾部:

let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome 現(xiàn)在等于 "hello there!"

注意:您不能將一個(gè)字符串或者字符添加到一個(gè)已經(jīng)存在的字符變量上,因?yàn)樽址兞恐荒馨粋€(gè)字符。
如果你需要使用多行字符串字面量來(lái)拼接字符串,并且你需要字符串每一行都以換行符結(jié)尾,包括最后一行:

let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
// 打印兩行:
// one
// twothree

let goodStart = """
one
two

"""
print(goodStart + end)
// 打印三行:
// one
// two
// three

上面的代碼,把 badStart 和 end 拼接起來(lái)的字符串非我們想要的結(jié)果。因?yàn)?badStart 最后一行沒(méi)有換行符,它與 end 的第一行結(jié)合到了一起。相反的,goodStart 的每一行都以換行符結(jié)尾,所以它與 end 拼接的字符串總共有三行,正如我們期望的那樣。

訪問(wèn)和修改字符串

你可以通過(guò)字符串的屬性和方法來(lái)訪問(wèn)和修改它,當(dāng)然也可以用下標(biāo)語(yǔ)法完成。

  • 字符串索引

每一個(gè)String值都有一個(gè)關(guān)聯(lián)的索引(index)類(lèi)型,String.Index,它對(duì)應(yīng)著字符串中的每一個(gè)Character的位置。

前面提到,不同的字符可能會(huì)占用不同數(shù)量的內(nèi)存空間,所以要知道Character的確定位置,就必須從String開(kāi)頭遍歷每一個(gè) Unicode 標(biāo)量直到結(jié)尾。因此,Swift 的字符串不能用整數(shù)(integer)做索引。

使用startIndex屬性可以獲取一個(gè)String的第一個(gè)Character的索引。使用endIndex屬性可以獲取最后一個(gè)Character的后一個(gè)位置的索引。因此,endIndex屬性不能作為一個(gè)字符串的有效下標(biāo)。如果String是空串,startIndex和endIndex是相等的。

通過(guò)調(diào)用 String 的 index(before:)index(after:) 方法,可以立即得到前面或后面的一個(gè)索引。您還可以通過(guò)調(diào)用 index(_:offsetBy:) 方法來(lái)獲取對(duì)應(yīng)偏移量的索引,這種方式可以避免多次調(diào)用 index(before:)index(after:) 方法。你可以使用下標(biāo)語(yǔ)法來(lái)訪問(wèn) String 特定索引的 Character。

let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a

試圖獲取越界索引對(duì)應(yīng)的 Character,將引發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤。

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

可以使用String.prefix(_:)String.suffix(_:)截取開(kāi)始或結(jié)尾的某些字符串,相當(dāng)于index(_:offsetBy:)

let stringSub = "abcdefghijklmnopqrstuvwxyz"
        
//獲取第一個(gè)到第十個(gè)字符串
print(String(stringSub.prefix(10)))
let index_start = stringSub.index(stringSub.startIndex, offsetBy: 10)
print(String(stringSub[..<index_start]))
        
//獲取倒數(shù)十個(gè)字符
print(String(stringSub.suffix(10)))
let index_end = stringSub.index(stringSub.endIndex, offsetBy: -10)
print(String(stringSub[index_end..<stringSub.endIndex]))
        
//獲取區(qū)間字符串
print(cutOutString(str: stringSub, range: 1..<5))
func cutOutString(str:String, range:Range<Int>) -> String
{
    if range.lowerBound < 0 || range.upperBound > str.count || str.count == 0
    {
        return ""
    }
    let start_index = str.index(str.startIndex, offsetBy: range.lowerBound)
    let end_index = str.index(str.startIndex, offsetBy: range.upperBound)
        
    return String(str[start_index..<end_index])
}

使用 indices 屬性會(huì)創(chuàng)建一個(gè)包含全部索引的范圍(Range),用來(lái)在一個(gè)字符串中訪問(wèn)單個(gè)字符。

for index in greeting.indices {
   print("\(greeting[index]) ", terminator: "")
}
// 打印輸出 "G u t e n T a g ! "

注意:您可以使用 startIndexendIndex 屬性或者 index(before:) 、index(after:)index(_:offsetBy:) 方法在任意一個(gè)確認(rèn)的并遵循 Collection 協(xié)議的類(lèi)型里面,如上文所示是使用在 String 中,您也可以使用在 Array、Dictionary 和 Set中。

  • 插入和刪除

調(diào)用 insert(_:at:) 方法可以在一個(gè)字符串的指定索引插入一個(gè)字符,調(diào)用 insert(contentsOf:at:) 方法可以在一個(gè)字符串的指定索引插入一個(gè)段字符串。

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 變量現(xiàn)在等于 "hello!"

welcome.insert(contentsOf:" there", at: welcome.index(before: welcome.endIndex))
// welcome 變量現(xiàn)在等于 "hello there!"

調(diào)用 remove(at:) 方法可以在一個(gè)字符串的指定索引刪除一個(gè)字符,調(diào)用 removeSubrange(_:) 方法可以在一個(gè)字符串的指定索引刪除一個(gè)子字符串。

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 現(xiàn)在等于 "hello there"

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

注意: 您可以使用 insert(_:at:)、insert(contentsOf:at:)remove(at:)removeSubrange(_:) 方法在任意一個(gè)確認(rèn)的并遵循 RangeReplaceableCollection 協(xié)議的類(lèi)型里面,如上文所示是使用在 String 中,您也可以使用在 Array、Dictionary 和 Set 中。

子字符串

當(dāng)你從字符串中獲取一個(gè)子字符串 —— 例如,使用下標(biāo)或者 prefix(_:) 之類(lèi)的方法 —— 就可以得到一個(gè) SubString 的實(shí)例,而非另外一個(gè) String。Swift 里的 SubString 絕大部分函數(shù)都跟 String 一樣,意味著你可以使用同樣的方式去操作 SubString 和 String。然而,跟 String 不同的是,你只有在短時(shí)間內(nèi)需要操作字符串時(shí),才會(huì)使用 SubString。當(dāng)你需要長(zhǎng)時(shí)間保存結(jié)果時(shí),就把 SubString 轉(zhuǎn)化為 String 的實(shí)例:

let greeting = "Hello, world!"
let index = greeting.index(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning 的值為 "Hello"

// 把結(jié)果轉(zhuǎn)化為 String 以便長(zhǎng)期存儲(chǔ)。
let newString = String(beginning)

就像 String,每一個(gè) SubString 都會(huì)在內(nèi)存里保存字符集。而 String 和 SubString 的區(qū)別在于性能優(yōu)化上,SubString 可以重用原 String 的內(nèi)存空間,或者另一個(gè) SubString 的內(nèi)存空間(String 也有同樣的優(yōu)化,但如果兩個(gè) String 共享內(nèi)存的話,它們就會(huì)相等)。這一優(yōu)化意味著你在修改 String 和 SubString 之前都不需要消耗性能去復(fù)制內(nèi)存。就像前面說(shuō)的那樣,SubString 不適合長(zhǎng)期存儲(chǔ) —— 因?yàn)樗赜昧嗽?String 的內(nèi)存空間,原 String 的內(nèi)存空間必須保留直到它的 SubString 不再被使用為止。上面的例子,greeting 是一個(gè) String,意味著它在內(nèi)存里有一片空間保存字符集。而由于 beginning 是 greeting 的 SubString,它重用了 greeting 的內(nèi)存空間。相反,newString 是一個(gè) String —— 它是使用 SubString 創(chuàng)建的,擁有一片自己的內(nèi)存空間。下面的圖展示了他們之間的關(guān)系:
https://share.weiyun.com/5QZfGPb

String與SubString的關(guān)系圖

關(guān)于 String 與 SubString 關(guān)系的文章:http://www.itdecent.cn/p/ff8c099d23b1

注意:String 和 SubString 都遵循 StringProtocol<//apple_ref/swift/intf/s:s14StringProtocolP> 協(xié)議,這意味著操作字符串的函數(shù)使用 StringProtocol 會(huì)更加方便。你可以傳入 String 或 SubString 去調(diào)用函數(shù)。

比較字符串

Swift 提供了三種方式來(lái)比較文本值:字符串字符相等、前綴相等和后綴相等。

  • 字符串/字符相等

字符串/字符可以用等于操作符(==)和不等于操作符(!=),詳細(xì)描述在比較運(yùn)算符

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"

如果兩個(gè)字符串(或者兩個(gè)字符)的可擴(kuò)展的字形群集是標(biāo)準(zhǔn)相等的,那就認(rèn)為它們是相等的。在這個(gè)情況下,即使可擴(kuò)展的字形群集是有不同的 Unicode 標(biāo)量構(gòu)成的,只要它們有同樣的語(yǔ)言意義和外觀,就認(rèn)為它們標(biāo)準(zhǔn)相等。例如,LATIN SMALL LETTER E WITH ACUTE(U+00E9)就是標(biāo)準(zhǔn)相等于LATIN SMALL LETTER E(U+0065)后面加上COMBINING ACUTE ACCENT(U+0301)。這兩個(gè)字符群集都是表示字符é的有效方式,所以它們被認(rèn)為是標(biāo)準(zhǔn)相等的:

// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"

// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"

if eAcuteQuestion == combinedEAcuteQuestion {
    print("These two strings are considered equal")
}
// 打印輸出 "These two strings are considered equal"

相反,英語(yǔ)中的LATIN CAPITAL LETTER A(U+0041,或者A)不等于俄語(yǔ)中的CYRILLIC CAPITAL LETTER A(U+0410,或者A)。兩個(gè)字符看著是一樣的,但卻有不同的語(yǔ)言意義:

let latinCapitalLetterA: Character = "\u{41}"

let cyrillicCapitalLetterA: Character = "\u{0410}"

if latinCapitalLetterA != cyrillicCapitalLetterA {
    print("These two characters are not equivalent")
}
// 打印 "These two characters are not equivalent"

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

關(guān)于 String.compare(_:options:range:locale:)方法的使用:講解鏈接

  1. options:
    NSCaseInsensitiveSearch:
    忽略大小寫(xiě)
    NSLiteralSearch:
    逐字節(jié)比較
    NSBackwardsSearch:
    執(zhí)行從范圍的末尾到開(kāi)頭的搜索
    NSAnchoredSearch:
    只對(duì)開(kāi)頭的字符執(zhí)行搜索,如果還指定了NSBackwardsSearch,則在范圍的末尾執(zhí)行搜索。開(kāi)頭或結(jié)尾不匹配意味著什么也找不到,即使字符串的其他地方出現(xiàn)了匹配的字符序列。
    NSNumericSearch:
    與...一起使用時(shí) compare:options:方法,數(shù)字組被視為數(shù)值,以便進(jìn)行比較。例如,F(xiàn)ilename9.txt< Filename20.txt< Filename100.txt。

  2. range:
    接收器中要搜索的范圍

  3. locale:
    比較接收方和aString時(shí)要使用的區(qū)域設(shè)置。要使用當(dāng)前語(yǔ)言環(huán)境,請(qǐng)傳遞[NSLocale current]。要使用系統(tǒng)語(yǔ)言環(huán)境,請(qǐng)傳遞nil。

let a = "String"
let b = "string"
        
let result2 = a.compare(b, options: .caseInsensitive, range: nil, locale: nil);
print(result2.rawValue)
        
let startIndex = a.index(a.startIndex, offsetBy: 1)
let result3 = a.compare(b, options: .literal, range: (startIndex..<a.endIndex), locale: nil);
print(result3.rawValue)

  • 前綴/后綴相等

通過(guò)調(diào)用字符串的hasPrefix(_:)hasSuffix(_:)方法來(lái)檢查字符串是否擁有特定前綴或后綴,兩個(gè)方法均接收一個(gè)String類(lèi)型的參數(shù),并返回一個(gè)布爾值。
下面的例子以一個(gè)字符串?dāng)?shù)組表示莎士比亞話劇《羅密歐與朱麗葉》中前兩場(chǎng)的場(chǎng)景位置:

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"
]

您可以調(diào)用hasPrefix(_:)方法來(lái)計(jì)算話劇中第一幕的場(chǎng)景數(shù):

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"

相似地,您可以用hasSuffix(_:)方法來(lái)計(jì)算發(fā)生在不同地方的場(chǎng)景數(shù):

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"

注意:hasPrefix(_:)hasSuffix(_:)方法都是在每個(gè)字符串中逐字符比較其可擴(kuò)展的字符群集是否標(biāo)準(zhǔn)相等,詳細(xì)描述在字符串/字符相等。

字符串的 Unicode 表示形式

Unicode是一個(gè)國(guó)際標(biāo)準(zhǔn),用于文本的編碼和表示。 它使您可以用標(biāo)準(zhǔn)格式表示來(lái)自任意語(yǔ)言幾乎所有的字符,并能夠?qū)ξ谋疚募蚓W(wǎng)頁(yè)這樣的外部資源中的字符進(jìn)行讀寫(xiě)操作。 Swift 的String和Character類(lèi)型是完全兼容 Unicode 標(biāo)準(zhǔn)的。

當(dāng)一個(gè) Unicode 字符串被寫(xiě)進(jìn)文本文件或者其他儲(chǔ)存時(shí),字符串中的 Unicode 標(biāo)量會(huì)用 Unicode 定義的幾種編碼格式(encoding forms)編碼。每一個(gè)字符串中的小塊編碼都被稱(chēng)代碼單元(code units)。這些包括 UTF-8 編碼格式(編碼字符串為8位的代碼單元), UTF-16 編碼格式(編碼字符串位16位的代碼單元),以及 UTF-32 編碼格式(編碼字符串32位的代碼單元)。

Swift 的String類(lèi)型是基于 Unicode 標(biāo)量 建立的。 Unicode 標(biāo)量 是對(duì)應(yīng)字符或者修飾符的唯一的21位數(shù)字,例如U+0061表示小寫(xiě)的拉丁字母(LATIN SMALL LETTER A)("a"),U+1F425表示小雞表情(FRONT-FACING BABY CHICK) ("??")。

Swift 提供了幾種不同的方式來(lái)訪問(wèn)字符串的 Unicode 表示形式。 您可以利用for-in來(lái)對(duì)字符串進(jìn)行遍歷,從而以 Unicode 可擴(kuò)展的字符群集的方式訪問(wèn)每一個(gè)Character值。 該過(guò)程在 使用字符 中進(jìn)行了描述。

另外,能夠以其他三種 Unicode 兼容的方式訪問(wèn)字符串的值:

  1. UTF-8 代碼單元集合 (利用字符串的utf8屬性進(jìn)行訪問(wèn))
  2. UTF-16 代碼單元集合 (利用字符串的utf16屬性進(jìn)行訪問(wèn))
  3. 21位的 Unicode 標(biāo)量值集合,也就是字符串的 UTF-32 編碼格式 (利用字符串的unicodeScalars屬性進(jìn)行訪問(wèn))

注意:Unicode 碼位(code poing) 的范圍是U+0000到U+D7FF或者U+E000到U+10FFFF。Unicode 標(biāo)量不包括 Unicode 代理項(xiàng)(surrogate pair) 碼位,其碼位范圍是U+D800到U+DFFF。

注意:不是所有的21位 Unicode 標(biāo)量都代表一個(gè)字符,因?yàn)橛幸恍?biāo)量是留作未來(lái)分配的。已經(jīng)代表一個(gè)典型字符的標(biāo)量都有自己的名字,例如上面例子中的LATIN SMALL LETTER A和FRONT-FACING BABY CHICK。

下面由D,o,g,?(DOUBLE EXCLAMATION MARK, Unicode 標(biāo)量 U+203C)和??(DOG FACE,Unicode 標(biāo)量為U+1F436)組成的字符串中的每一個(gè)字符代表著一種不同的表示:

let dogString = "Dog???"
  • UTF-8 表示

您可以通過(guò)遍歷String的utf8屬性來(lái)訪問(wèn)它的UTF-8表示。 其為String.UTF8View類(lèi)型的屬性,UTF8View是無(wú)符號(hào)8位 (UInt8) 值的集合,每一個(gè)UInt8值都是一個(gè)字符的 UTF-8 表示:
https://share.weiyun.com/50OPwUr

UTF-8表示.png

for codeUnit in dogString.utf8 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// 68 111 103 226 128 188 240 159 144 182

上面的例子中,前三個(gè)10進(jìn)制codeUnit值 (68, 111, 103) 代表了字符D、o和 g,它們的 UTF-8 表示與 ASCII 表示相同。 接下來(lái)的三個(gè)10進(jìn)制codeUnit值 (226, 128, 188) 是DOUBLE EXCLAMATION MARK的3字節(jié) UTF-8 表示。 最后的四個(gè)codeUnit值 (240, 159, 144, 182) 是DOG FACE的4字節(jié) UTF-8 表示。

  • UTF-16 表示

您可以通過(guò)遍歷String的utf16屬性來(lái)訪問(wèn)它的UTF-16表示。 其為String.UTF16View類(lèi)型的屬性,UTF16View是無(wú)符號(hào)16位 (UInt16) 值的集合,每一個(gè)UInt16都是一個(gè)字符的 UTF-16 表示:
https://share.weiyun.com/5ITKXCb

UTF-16表示.png

for codeUnit in dogString.utf16 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// 68 111 103 8252 55357 56374

同樣,前三個(gè)codeUnit值 (68, 111, 103) 代表了字符D、o和g,它們的 UTF-16 代碼單元和 UTF-8 完全相同(因?yàn)檫@些 Unicode 標(biāo)量表示 ASCII 字符)。第四個(gè)codeUnit值 (8252) 是一個(gè)等于十六進(jìn)制203C的的十進(jìn)制值。這個(gè)代表了DOUBLE EXCLAMATION MARK字符的 Unicode 標(biāo)量值U+203C。這個(gè)字符在 UTF-16 中可以用一個(gè)代碼單元表示。第五和第六個(gè)codeUnit值 (55357和56374) 是DOG FACE字符的 UTF-16 表示。 第一個(gè)值為U+D83D(十進(jìn)制值為55357),第二個(gè)值為U+DC36(十進(jìn)制值為56374)。

  • Unicode 標(biāo)量表示

您可以通過(guò)遍歷String值的unicodeScalars屬性來(lái)訪問(wèn)它的 Unicode 標(biāo)量表示。 其為UnicodeScalarView類(lèi)型的屬性,UnicodeScalarView是UnicodeScalar類(lèi)型的值的集合。 UnicodeScalar是21位的 Unicode 代碼點(diǎn)。每一個(gè)UnicodeScalar擁有一個(gè)value屬性,可以返回對(duì)應(yīng)的21位數(shù)值,用UInt32來(lái)表示:
https://share.weiyun.com/5x7TkkQ

Unicode 標(biāo)量表示.png

for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
print("")
// 68 111 103 8252 128054

前三個(gè)UnicodeScalar值(68, 111, 103)的value屬性仍然代表字符D、o和g。 第四個(gè)codeUnit值(8252)仍然是一個(gè)等于十六進(jìn)制203C的十進(jìn)制值。這個(gè)代表了DOUBLE EXCLAMATION MARK字符的 Unicode 標(biāo)量U+203C。第五個(gè)UnicodeScalar值的value屬性,128054,是一個(gè)十六進(jìn)制1F436的十進(jìn)制表示。其等同于DOG FACE的 Unicode 標(biāo)量U+1F436。
作為查詢(xún)它們的value屬性的一種替代方法,每個(gè)UnicodeScalar值也可以用來(lái)構(gòu)建一個(gè)新的String值,比如在字符串插值中使用:

for scalar in dogString.unicodeScalars {
    print("\(scalar) ")
}
// D
// o
// g
// ?
// ??

獲取單個(gè)字符的Unicode碼

let strScalar = UnicodeScalar("a")?.value ?? 0
print(strScalar)  //97

可擴(kuò)展的字形群集

每一個(gè) Swift 的Character類(lèi)型代表一個(gè)可擴(kuò)展的字形群。 一個(gè)可擴(kuò)展的字形群是一個(gè)或多個(gè)可生成人類(lèi)可讀的字符 Unicode 標(biāo)量的有序排列。舉個(gè)例子,字母é可以用單一的 Unicode 標(biāo)量é(LATIN SMALL LETTER E WITH ACUTE, 或者U+00E9)來(lái)表示。然而一個(gè)標(biāo)準(zhǔn)的字母e(LATIN SMALL LETTER E或者U+0065) 加上一個(gè)急促重音(COMBINING ACTUE ACCENT)的標(biāo)量(U+0301),這樣一對(duì)標(biāo)量就表示了同樣的字母é。 這個(gè)急促重音的標(biāo)量形象的將e轉(zhuǎn)換成了é。在這兩種情況中,字母é代表了一個(gè)單一的 Swift 的Character值,同時(shí)代表了一個(gè)可擴(kuò)展的字形群。 在第一種情況,這個(gè)字形群包含一個(gè)單一標(biāo)量;而在第二種情況,它是包含兩個(gè)標(biāo)量的字形群:

let eAcute: Character = "\u{E9}"                         // é
let combinedEAcute: Character = "\u{65}\u{301}"          // e 后面加上 ?
// eAcute 是 é, combinedEAcute 是 é

可擴(kuò)展的字符群集是一個(gè)靈活的方法,用許多復(fù)雜的腳本字符表示單一的Character值。 例如,來(lái)自朝鮮語(yǔ)字母表的韓語(yǔ)音節(jié)能表示為組合或分解的有序排列。 在 Swift 都會(huì)表示為同一個(gè)單一的Character值:

let precomposed: Character = "\u{D55C}"                  // ?
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ?, ?, ?
// precomposed 是 ?, decomposed 是 ?

可拓展的字符群集可以使包圍記號(hào)(例如COMBINING ENCLOSING CIRCLE或者U+20DD)的標(biāo)量包圍其他 Unicode 標(biāo)量,作為一個(gè)單一的Character值:

let enclosedEAcute: Character = "\u{E9}\u{20DD}"

enclosedEAcute 是

地域性指示符號(hào)的 Unicode 標(biāo)量可以組合成一個(gè)單一的Character值,例如REGIONAL INDICATOR SYMBOL LETTER U(U+1F1FA)和REGIONAL INDICATOR SYMBOL LETTER S(U+1F1F8):

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS 是 ????
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容